1. Python中的旧式类与新式类
你一定在其它地方见过这样的表述,“在 Python 中,一切皆对象”。
那么,它意味着什么呢?是不是像其它编程语言一样(例如 Java),在 Python 中的一切都是基类的实例?如果是,那么 Python 中的基类是什么?我听说过 Python 中的 object 类,它是那个基类吗?
Python 中这一块的内容相当复杂。Python 有旧式类和新式类两种模型。事实上,在早期 Python 版本中,Python 对于任何对象都没有显示的基类。但从 2.2 版本开始,引入了新式类,我们可以(或者必须)让每个对象都是 object 的子类。
直到 Python 2.1 ,旧式类还一直是用户唯一可用的风格。旧式类与类型的概念无关。如果 x 是旧式类的实例,x.class 将应用 x 所属的类。但 type(x) 并不如此。我们来看一下下面的代码。
新样式类是从 Python 2.2 引入的。 它旨在为 Python 提供一个统一的对象模型。 在新式类中,引入了 object 类。 一切都继承自 object 。
新式类是通过继承自 object 类来定义的。 与旧式类不同,如果x是新样式类的实例,那么 x.class 和 type(x) 都指x所属的类。
为了与老版本兼容,Python 中的类仍然默认使用旧式类。 如果我们想使用新式类,我们需要定义类为 object 的子类。
2. 旧式类与新式类的不同
旧式类与新式类之间的显著不同在于类型系统。
我们来看一下旧式类和新式类处理多重继承。 “多重继承”是指一个类可以继承自多个类。 如果A继承自B,A是B的子类(派生类),B是A的超类(基类或者父类)。
多重继承允许一个类A可以有很多父类(我个人不喜欢多重继承,当一个类可以有很多父类的时候,它会变得相当奇怪。为了解决这个问题,其他编程语言支持另外的模型,像 Java 中的接口 或者是 Ruby 中的混入。就我个人而言,我非常喜欢Ruby的混入模型)。
在 Python 对象模型中,每个类都有基类属性,它们按继承中的出现顺序存储所有的父类。
那么,多重继承有什么问题呢?
多重继承的问题是超类的顺序。 为什么继承顺序很重要? 因为当子类的实例访问属性(变量或函数)时。 首先,它会在自己的空间来查找该属性,如果未找到该属性,它将继续查找类空间。 如果仍然找不到,搜索方法继续超类空间。 当一个类有许多超类时,超类的顺序就是搜索方法中的顺序。 在旧式类中,超类的顺序是深度优先,从左到右按照基类列表中的出现顺序。
旧式类方法的解析相当简单易懂,对吧? 但是,如果我们遵循这种规则,有时我们会在继承时犯错误。 例如,我们创建一个类G继承自A,D和E,而A是D和E的父类。它应该在这个上下文中抛出一个错误,以防止像这样的循环继承。
新式类解决了这个问题。 新式类从 Python 2.3 引入了 MRO(方法解析顺序)。 MRO 的算法可以描述如下
def mro(cls):
"""
Return ordering of superclass of cls
This ordering was used when we want to access class instance atrribute
`cls`: class type we want to resolve
@raise `TypeError` if cannot resolution superclass order
@return `list` of class
"""
bases_cls = cls.__bases__
mro_base_lists = [mro(base_cls) for base_cls in bases_cls]
mro_list = [cls]
while mro_base_lists:
# find the good head
# good head is head of a list which is not is tail of any list in mro_base_lists
list_head = (x[0] for x in mro_base_lists)
set_tails = set()
for x in mro_base_lists:
set_tails.update(x[1:])
good_head = None
for head in list_head:
if head not in set_tails:
good_head = head
break
# if cannot find good_head, raise TypeError
if not good_head:
raise TypeError
else:
# add to mro_list
mro_list.append(good_head)
# remove good_head in all list and add to mro_list
for alist in mro_base_lists:
try:
alist.remove(good_head)
except Exception:
pass
mro_base_lists = [x for x in mro_base_lists if x]
return mro_list
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
class E(C, B): pass
class F(D, E): pass
def test_mro():
assert mro(A) == [A]
print "Test1 passed"
assert mro(B) == [B, A]
print "Test2 passed"
assert mro(C) == [C, A]
print "Test 3 passed"
assert mro(D) == [D, B, C, A]
print "Test 4 passed"
assert mro(E) == [E, C, B, A]
print "Test 5 passed"
try:
mro(F)
except Exception as e:
assert isinstance(e, TypeError)
print "Test 6 passed"
test_mro()
MRO算法的思想源于基类排序情况:如果B是C的子类,B总是在列表中的C之前。 这就是为什么我们收到一个 TypeErorr 如果我们定义类D(B,C)和E(C,B)之后定义F(D,E)。
想更多的了解 MRO 是如何工作的,你应该参阅 The Python 2.3 Method Resolution Order。
新式类和旧式类的不同不仅体现在基类顺序方面。 我会在下一篇文章中介绍这个主题。
原文链接: Python Object Model 。