新式类与旧式类
Python 2 中存在两种类:旧式类(classic classes)和新式类(new-style classes)。这是 Python 2 最混乱的设计之一,也是 Python 3 彻底移除旧式类的原因。理解两者的区别,对写出正确的 Python 2 代码至关重要。
语法区别
# 旧式类(Python 2 默认)
class OldStyle:
pass
# 新式类(必须显式继承 object)
class NewStyle(object):
pass
print type(OldStyle()) # <type 'instance'>
print type(NewStyle()) # <class '__main__.NewStyle'>
旧式类不继承任何类(或者说继承自一个隐式的 instance 类型),新式类显式继承 object。
核心差异
| 特性 | 旧式类 | 新式类 |
|---|---|---|
| 类型 | type 'instance' | type 'class' |
| MRO | 深度优先 | C3 线性化 |
super() | 不支持 | 支持 |
__getattribute__ | 不支持 | 支持 |
property | 不支持 | 支持 |
| 描述符 | 不支持 | 支持 |
__slots__ | 不支持 | 支持 |
| 多重继承 | 行为不可预测 | 行为明确 |
为什么旧式类存在
旧式类是 Python 1.x 的遗产。Python 2.2 引入新式类时,为了保持向后兼容,旧式类仍然作为默认行为保留。Python 3 中所有类都是新式类,旧式类被彻底移除。
实际影响
1. super() 不可用
class Base:
def method(self):
return "Base"
class Derived(Base):
def method(self):
# super(Derived, self).method() # TypeError: super() argument 1 must be type, not classobj
return Base.method(self) + " -> Derived"
旧式类中必须用 Base.method(self) 调用父类方法。
2. property 不可用
class Circle:
@property # 旧式类中 property 不工作!
def area(self):
return 3.14 * self.r ** 2
c = Circle()
c.r = 5
print c.area # <property object>,不是计算结果!
旧式类中 property 只是返回 property 对象本身,不会拦截属性访问。
3. 方法解析顺序不同
class A:
def method(self):
return "A"
class B(A):
def method(self):
return "B"
class C(A):
def method(self):
return "C"
class D(B, C):
pass
d = D()
print d.method() # B(旧式类深度优先)
旧式类的 MRO 是 D -> B -> A -> C,而新式类是 D -> B -> C -> A。
始终使用新式类
Python 2 中,始终显式继承 object:
# 正确
class Animal(object):
pass
class Dog(Animal):
pass
# 错误(旧式类)
class Animal:
pass
这是 Python 2 编程的铁律。不继承 object 的类会失去 super()、property、描述符等现代特性,而且多重继承行为不可预测。
类型检查的差异
class Old:
pass
class New(object):
pass
o = Old()
n = New()
print type(o) == Old # False!旧式类的 type 不是类本身
print type(n) == New # True
print isinstance(o, Old) # True
print isinstance(n, New) # True
旧式类中 type(instance) == Class 不成立,因为 type() 返回 instance 而非类。始终用 isinstance() 进行类型检查。
向 Python 3 迁移
Python 3 中所有类都是新式类,以下代码在 Python 2 和 3 中都有效:
class Animal(object):
pass
class Dog(Animal):
pass
如果 Python 2 代码中有不继承 object 的类,迁移到 Python 3 时只需加上 (object),行为基本不变(除了 type() 返回值变化)。