Python中的旧样式类和新样式类有什么区别?

Python中的旧样式类和新样式类有什么区别?什么时候我应该用一种或另一种?

263488 次浏览

从# 0:

在Python 2.1之前,旧式类是用户唯一可用的风格。

(老式)类的概念与类型的概念无关:如果x是一个旧式类的实例,则x.__class__指定x的类,但type(x)总是<类型“实例”祝辞> < /代码。< / p >

这反映了一个事实,即所有旧式实例,独立于它们的类是用一个内置类型实现的,称为实例。< / p > < p > # 0。新风格的类只是一个用户定义的类型,不多不少 如果x是一个new-style类的实例,那么type(x)通常是与x.__class__相同(尽管这不能保证- aNew-style类实例允许重写返回的值# 0)。< / p >

# 0。

它也有一些直接的好处,比如能够大多数内置类型的子类,或者“描述符”的引入,

# 0。

新建样式类通过指定另一个新建样式类来创建(即类型)作为父类,或者“顶级类型”对象(如果没有)需要另一位家长。< / p > 新式类的行为不同于旧式类类中一些重要的细节除了什么类型的回报。< / p > 其中一些更改是新对象模型的基础,例如调用特殊方法的方式。其他的则是“修复”,无法修复出于兼容性考虑,应该像方法一样在之前实现在多重继承情况下的解析顺序

# 0。

不管你是否从object继承子类,类都是new-style的3.

New-style类继承自object,并且在Python 2.2以后必须这样编写(即class Classname(object):而不是class Classname:)。核心变化是统一类型和类,这样做的好处是允许从内置类型继承。

阅读descrintro了解更多细节。

Declaration-wise:

New-style类继承自对象或另一个New-style类。

class NewStyleClass(object):pass
class AnotherNewStyleClass(NewStyleClass):pass

老式的类没有。

class OldStyleClass():pass

注意事项:

Python 3不支持旧样式的类,所以上面提到的任何一种形式都会产生新样式的类。

对于属性查找,旧样式的类仍然稍微快一些。这通常并不重要,但在性能敏感的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.a10000000 loops, best of 3: 78.7 ns per loop
In [10]: %timeit bobj.a10000000 loops, best of 3: 86.9 ns per loop

Guido写了关于新型类的内幕故事,这是一篇关于Python中的新风格和旧风格类的非常棒的文章。

Python 3只有new-style类。即使你写了一个“老式类”,它也是隐式地从object派生的。

新型类有一些老式类所没有的高级特性,比如super、新的C3 mro、一些神奇的方法等等。

新样式类可以使用super(Foo, self),其中Foo是类,self是实例。

# 0

返回一个代理对象,该对象将方法调用委托给类型的父类或兄弟类。这对于访问在类中被重写的继承方法非常有用。除了类型本身被跳过之外,搜索顺序与getattr()所使用的相同。

在Python 3中。X,您可以简单地在类中使用super()而不带任何参数。

这里有一个非常实际的真/假的区别。以下代码的两个版本之间的唯一区别是,在第二个版本中继承了对象。除此之外,这两个版本是相同的,但结果不同:

  1. < p >老式类

    class Person():_names_cache = {}def __init__(self,name):self.name = namedef __new__(cls,name):return cls._names_cache.setdefault(name,object.__new__(cls,name))
    ahmed1 = Person("Ahmed")ahmed2 = Person("Ahmed")print ahmed1 is ahmed2print ahmed1print ahmed2
    
    >>> False<__main__.Person instance at 0xb74acf8c><__main__.Person instance at 0xb74ac6cc>>>>
    
  2. New-style classes

    class Person(object):_names_cache = {}def __init__(self,name):self.name = namedef __new__(cls,name):return cls._names_cache.setdefault(name,object.__new__(cls,name))
    ahmed1 = Person("Ahmed")ahmed2 = Person("Ahmed")print ahmed2 is ahmed1print ahmed1print ahmed2
    >>> True<__main__.Person object at 0xb74ac66c><__main__.Person object at 0xb74ac66c>>>>

新旧样式类之间的重要行为变化

  • # 0添加
  • MRO变更(解释如下)
  • # 0添加
  • 除非从Exception派生,否则不能引发新的样式类对象(下面的示例)
  • # 1添加

MRO(方法解决顺序)更改

它在其他回答中提到过,但这里有一个经典MRO和C3 MRO之间区别的具体例子(用于新风格的职业)。

问题是在多重继承中搜索属性(包括方法和成员变量)的顺序。

经典的类从左到右进行深度优先搜索。停在第一根火柴上。它们没有__mro__属性。

class C: i = 0class C1(C): passclass C2(C): i = 2class C12(C1, C2): passclass C21(C2, C1): pass
assert C12().i == 0assert C21().i == 2
try:C12.__mro__except AttributeError:passelse:assert False

新型类 MRO在一个英语句子中合成要复杂得多。详细的解释在在这里中。它的一个属性是只搜索一次基类的所有派生类。它们有__mro__属性,显示搜索顺序。

class C(object): i = 0class C1(C): passclass C2(C): i = 2class C12(C1, C2): passclass C21(C2, C1): pass
assert C12().i == 2assert 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: passtry:raise Old()except Old:passelse:assert False
# TypeError, new not derived from `Exception`.class New(object): passtry:raise New()except TypeError:passelse:assert False
# OK, derived from `Exception`.class New(Exception): passtry:raise New()except New:passelse:assert False
# `'str'` is a new style object, so you can't raise it:try:raise 'str'except TypeError:passelse:assert False