Python中的旧样式和新样式类有什么区别?我什么时候应该使用其中一种?
来自http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes:
在Python 2.1中,旧式类是用户可用的唯一风格.
(旧式)类的概念与类型的概念无关:如果
x
是旧式类的实例,则x.__class__
指定类的类x
,但type(x)
始终是.
这反映了这样一个事实,即所有旧式实例独立于其类,都使用一个称为实例的内置类型实现.
Python 2.2中引入了新式类,以统一类和类型的概念.新式类只是用户定义的类型,不多也不少.
如果x是新样式类的实例,那么
type(x)
通常是相同的x.__class__
(尽管不能保证 - 允许新样式类实例覆盖返回的值x.__class__
).引入新式类的主要动机是提供具有完整元模型的统一对象模型.
它还具有许多直接的好处,例如能够对大多数内置类型进行子类化,或引入"描述符",从而启用计算属性.
出于兼容性原因,默认情况下类仍为旧式.
通过将另一个新样式类(即类型)指定为父类来创建新样式类,或者如果不需要其他父类,则创建"顶级类型"对象.
除了返回的类型之外,新样式类的行为在许多重要细节中与旧样式类的行为不同.
其中一些更改是新对象模型的基础,就像调用特殊方法的方式一样.其他是针对兼容性问题之前无法实现的"修复",例如在多重继承的情况下的方法解析顺序.
Python 3只有新式的类.
无论你是否是子类
object
,类都是Python 3中的新风格.
宣言明智的:
新式类继承自object或其他新式类.
class NewStyleClass(object): pass class AnotherNewStyleClass(NewStyleClass): pass
旧式的课程没有.
class OldStyleClass(): pass
旧样式类和新样式类之间的重要行为更改
超级补充
MRO改变了(下面解释)
描述符添加
除非派生自Exception
(以下示例),否则无法引发新样式类对象
__slots__
添加
在其他答案中提到过,但这里是经典MRO和C3 MRO(用于新风格类别)之间差异的具体例子.
问题是在多重继承中搜索属性(包括方法和成员变量)的顺序.
经典课程从左到右进行深度优先搜索.第一场比赛停止.他们没有__mro__
属性.
class C: i = 0 class C1(C): pass class C2(C): i = 2 class C12(C1, C2): pass class C21(C2, C1): pass assert C12().i == 0 assert C21().i == 2 try: C12.__mro__ except AttributeError: pass else: assert False
新式 MRO在单个英语句子中合成更复杂.这里将详细解释.它的一个属性是Base类只在其所有Derived类都被搜索过.它们具有__mro__
显示搜索顺序的属性.
class C(object): i = 0 class C1(C): pass class C2(C): i = 2 class C12(C1, C2): pass class C21(C2, C1): pass assert C12().i == 2 assert C21().i == 2 assert C12.__mro__ == (C12, C1, C2, C, object) assert C21.__mro__ == (C21, C2, C1, C, object)
Exception
围绕Python 2.5,可以引发许多类,围绕Python 2.6,这被删除了.在Python 2.7.3上:
# OK, old: class Old: pass try: raise Old() except Old: pass else: assert False # TypeError, new not derived from `Exception`. class New(object): pass try: raise New() except TypeError: pass else: assert False # OK, derived from `Exception`. class New(Exception): pass try: raise New() except New: pass else: assert False # `'str'` is a new style object, so you can't raise it: try: raise 'str' except TypeError: pass else: assert False
对于属性查找,旧样式类仍然稍微快一些.这通常不重要,但在对性能敏感的Python 2.x代码中可能很有用:
In [3]: class A: ...: def __init__(self): ...: self.a = 'hi there' ...: In [4]: class B(object): ...: def __init__(self): ...: self.a = 'hi there' ...: In [6]: aobj = A() In [7]: bobj = B() In [8]: %timeit aobj.a 10000000 loops, best of 3: 78.7 ns per loop In [10]: %timeit bobj.a 10000000 loops, best of 3: 86.9 ns per loop
Guido写了一篇关于New-Style Classes的The Inside Story,这是一篇关于Python中新风格和旧式类的非常棒的文章.
Python 3只有新式的类,即使你写了一个'old-style class',它也是隐式派生的object
.
新式类具有一些缺乏旧式类的高级功能,例如super
新的C3 mro,一些神奇的方法等.
这是一个非常实用的,真/假的区别.以下代码的两个版本之间的唯一区别是,在第二个版本中Person继承自object.除此之外,两个版本相同,但结果不同:
1)旧式课程
class Person(): _names_cache = {} def __init__(self,name): self.name = name def __new__(cls,name): return cls._names_cache.setdefault(name,object.__new__(cls,name)) ahmed1 = Person("Ahmed") ahmed2 = Person("Ahmed") print ahmed1 is ahmed2 print ahmed1 print ahmed2 >>> False <__main__.Person instance at 0xb74acf8c> <__main__.Person instance at 0xb74ac6cc> >>>
2)新式课程
class Person(object): _names_cache = {} def __init__(self,name): self.name = name def __new__(cls,name): return cls._names_cache.setdefault(name,object.__new__(cls,name)) ahmed1 = Person("Ahmed") ahmed2 = Person("Ahmed") print ahmed2 is ahmed1 print ahmed1 print ahmed2 >>> True <__main__.Person object at 0xb74ac66c> <__main__.Person object at 0xb74ac66c> >>>
新样式类继承自object
Python 2.2之后必须编写(即class Classname(object):
代替class Classname:
).核心变化是统一类型和类,这样做的好处是它允许您从内置类型继承.
阅读descrintro了解更多详情.
新样式类可以使用super(Foo, self)
where Foo
类和self
实例.
super(type[, object-or-type])
返回将方法调用委托给父类或兄弟类类型的代理对象.这对于访问已在类中重写的继承方法很有用.搜索顺序与getattr()使用的搜索顺序相同,只是跳过了类型本身.
在Python 3.x中,您可以简单地super()
在没有参数的类中使用.