Python中的旧风格和新风格类有什么区别?
Python中的旧风格和新风格类有什么区别? 现在有没有理由使用旧式的课程?
从http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes :
在Python 2.1之前,老式的类是唯一可用的风格。 (old-style)类的概念与type的概念无关:如果
x
是旧式类的实例,则x.__class__
指定x
的类,但type(x)
始终是<type 'instance'>
。 这反映了这样一个事实:所有旧式实例独立于它们的类,都使用一个名为instance的内置types实现。在Python 2.2中引入了新式的类来统一类和types的概念 。 一个新式的类只是一个用户定义的types,不多也不less。 如果x是新样式类的实例,则
type(x)
通常与x.__class__
相同(尽pipe这不能保证 – 新样式类实例被允许覆盖为x.__class__
返回的值) 。引入新类的主要动机是提供一个完整的元模型的统一对象模型 。 它也有许多直接的好处,比如对大多数内置types进行子类化的能力,或者引入了可以计算属性的“描述符”。
出于兼容性原因,默认情况下类仍然是旧式的 。 通过将另一个新样式类(即一个types)指定为父类,或者如果不需要其他父类,则指定“顶级types”对象来创build新样式类。 除了什么types的返回之外,新风格类的行为与旧风格类的行为有很多重要的细节。 其中的一些变化对于新的对象模型来说是很重要的,就像调用特殊的方法一样。 其他的是“修复”,在兼容性问题之前无法实现,比如在多重inheritance情况下的方法parsing顺序。
Python 3只有新风格的类 。 无论你是否从子类inheritance,类都是Python 3中的新类。
宣言明智的:
新风格的类inheritance自对象,或从另一个新风格的类inheritance。
class NewStyleClass(object): pass class AnotherNewStyleClass(NewStyleClass): pass
老式的课程不。
class OldStyleClass(): pass
旧样式类和新样式类之间重要的行为变化
- 超级添加
- MRO改变(下面解释)
- 描述符添加
- 新的样式类对象不能被引发,除非派生自
Exception
(下面的例子) -
__slots__
添加
MRO(方法解决顺序)已更改
在其他答案中提到了这个问题,但这里有一个典型的MRO和C3 MRO(用于新风格类)之间区别的具体例子。
问题是在多重inheritance中search属性(包括方法和成员variables)的顺序。
经典类从左到右进行深度优先search。 第一场比赛停止。 他们没有__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更加复杂。 这里详细解释。 它的一个属性是一个基类只有一次search到所有的派生类。 他们有显示search顺序的__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代码中可能会有用:
在[3]中:A类: ...:def __init __(self): ...:self.a ='你好' ...: 在[4]中:class B(object): ...:def __init __(self): ...:self.a ='你好' ...: 在[6]中:aobj = A() 在[7]中:bobj = B() 在[8]中:%timeit aobj.a 10000000个循环,最好3个:每个循环78.7纳秒 在[10]中:%timeit bobj.a 10000000个循环,最好是每个循环86.9 ns
Guido写了New Style Class的“Inside Story” ,这是一篇关于Python中新式和旧式类的非常棒的文章。
Python 3只有新式的类,即使你写了一个“旧式类”,它也是隐含地从object
派生的。
新式class级有一些老式class级缺乏的先进特征,比如super
新型C3 mro ,一些神奇的方法等等。
这是一个非常实用,真实/错误的区别。 以下代码的两个版本之间的唯一区别是在第二个版本Person从对象inheritance。 除此之外,两个版本是相同的,但结果不同:
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> >>>
新的类inheritance自object
,必须在Python 2.2之后(即class Classname(object):
而不是class Classname:
编写。 核心的变化是统一types和类,这个好的副作用是它允许你从内置typesinheritance。
阅读descrintro了解更多详情。
新的风格类可能会使用super(Foo, self)
,其中Foo
是一个类, self
是实例。
super(type[, object-or-type])
返回一个将方法调用委托给父类或兄弟类的代理对象。 这对访问在类中被覆盖的inheritance方法很有用。 search顺序与getattr()使用的顺序相同,只是跳过了types本身。
而在Python 3.x中,您可以简单地在不带参数的类中使用super()
。
或者说,你应该总是使用新风格的类, 除非你的代码需要与2.2以前版本的Python一起工作。