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

问题:Python中旧样式类与新样式类有什么区别?

Python中旧样式类与新样式类有什么区别?什么时候应该使用其中一个?

What is the difference between old style and new style classes in Python? When should I use one or the other?


回答 0

新式和经典类

直到Python 2.1,旧式类才是供用户使用的唯一样式。

(旧式)类的概念与类型的概念无关:如果x是旧式类的实例,则x.__class__ 指定的类x,但type(x)始终为<type 'instance'>

这反映了这样一个事实,即所有旧式实例(独立于其类)均使用称为实例的单个内置类型实现。

在Python 2.2中引入了新型类,以统一类和类型的概念。新型类只是用户定义的类型,不多也不少。

如果x是新样式类的实例,则type(x)通常与x 相同x.__class__(尽管不能保证–允许新样式类实例覆盖所返回的值x.__class__)。

引入新型类的主要动机是提供具有完整元模型的统一对象模型

它还具有许多直接的好处,例如能够对大多数内置类型进行子类化,或者引入了“描述符”,以启用计算属性。

出于兼容性原因,默认情况下,类仍为旧样式

通过将另一个新样式类(即一种类型)指定为父类或“顶级类型”对象(如果不需要其他父类)来创建新样式类。

新样式类的行为与旧样式类的行为不同,除了返回什么类型外,还有许多重要的细节。

其中一些更改是新对象模型的基础,例如调用特殊方法的方式。其他是出于兼容性考虑而无法实现的“修补程序”,例如在多重继承的情况下的方法解析顺序。

Python 3仅具有新型类

无论是否从中继承子类object,类都是Python 3中的新型样式。

From New-style and classic classes:

Up to Python 2.1, old-style classes were the only flavour available to the user.

The concept of (old-style) class is unrelated to the concept of type: if x is an instance of an old-style class, then x.__class__ designates the class of x, but type(x) is always <type 'instance'>.

This reflects the fact that all old-style instances, independently of their class, are implemented with a single built-in type, called instance.

New-style classes were introduced in Python 2.2 to unify the concepts of class and type. A new-style class is simply a user-defined type, no more, no less.

If x is an instance of a new-style class, then type(x) is typically the same as x.__class__ (although this is not guaranteed – a new-style class instance is permitted to override the value returned for x.__class__).

The major motivation for introducing new-style classes is to provide a unified object model with a full meta-model.

It also has a number of immediate benefits, like the ability to subclass most built-in types, or the introduction of “descriptors”, which enable computed properties.

For compatibility reasons, classes are still old-style by default.

New-style classes are created by specifying another new-style class (i.e. a type) as a parent class, or the “top-level type” object if no other parent is needed.

The behaviour of new-style classes differs from that of old-style classes in a number of important details in addition to what type returns.

Some of these changes are fundamental to the new object model, like the way special methods are invoked. Others are “fixes” that could not be implemented before for compatibility concerns, like the method resolution order in case of multiple inheritance.

Python 3 only has new-style classes.

No matter if you subclass from object or not, classes are new-style in Python 3.


回答 1

声明方式:

新样式类继承自object或另一个新类。

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

老式的类没有。

class OldStyleClass():
    pass

Python 3注意:

Python 3不支持旧样式类,因此上述任何一种形式都会生成新样式类。

Declaration-wise:

New-style classes inherit from object, or from another new-style class.

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

Old-style classes don’t.

class OldStyleClass():
    pass

Python 3 Note:

Python 3 doesn’t support old style classes, so either form noted above results in a new-style class.


回答 2

新旧样式类之间的重要行为更改

  • 超级添加
  • MRO已更改(说明如下)
  • 添加了描述符
  • 除非派生自Exception(以下示例),否则不能引发新样式类对象
  • __slots__ 添加

MRO(方法解析顺序)已更改

它在其他答案中也提到过,但是这里有一个具体示例,说明了经典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在单个英语句子中合成起来更加复杂。在这里详细解释。它的特性之一是,只有在所有基类的派生类都被查找之后才搜索基类。它们具有__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

Important behavior changes between old and new style classes

  • super added
  • MRO changed (explained below)
  • descriptors added
  • new style class objects cannot be raised unless derived from Exception (example below)
  • __slots__ added

MRO (Method Resolution Order) changed

It was mentioned in other answers, but here goes a concrete example of the difference between classic MRO and C3 MRO (used in new style classes).

The question is the order in which attributes (which include methods and member variables) are searched for in multiple inheritance.

Classic classes do a depth-first search from left to right. Stop on the first match. They do not have the __mro__ attribute.

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

New-style classes MRO is more complicated to synthesize in a single English sentence. It is explained in detail here. One of its properties is that a base class is only searched for once all its derived classes have been. They have the __mro__ attribute which shows the search order.

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)

New style class objects cannot be raised unless derived from Exception

Around Python 2.5 many classes could be raised, and around Python 2.6 this was removed. On 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

回答 3

旧样式的类仍然比属性查找要快一些。这通常并不重要,但是在对性能敏感的Python 2.x代码中可能有用:

在[3]中:A类:
   ...:def __init __(self):
   ...:self.a ='hi there'
   ...:

在[4]中:B类(对象):
   ...:def __init __(self):
   ...:self.a ='hi there'
   ...:

在[6]中:aobj = A()
在[7]中:bobj = B()

在[8]中:%timeit aobj.a
10000000次循环,每循环3:78.7 ns最佳

在[10]中:%timeit bobj.a
10000000次循环,每循环3:86.9 ns最佳

Old style classes are still marginally faster for attribute lookup. This is not usually important, but it may be useful in performance-sensitive Python 2.x code:

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

回答 4

Guido撰写了有关New-Style Classes的The Inside Story,这是一篇有关Python中的新风格和旧风格类的非常不错的文章。

Python 3只有新样式的类。即使您编写了一个“旧类”,它也是从隐式派生的object

新式类具有一些旧式类所缺少的高级功能,例如super,新的C3 mro,一些神奇的方法等。

Guido has written The Inside Story on New-Style Classes, a really great article about new-style and old-style class in Python.

Python 3 has only new-style class. Even if you write an ‘old-style class’, it is implicitly derived from object.

New-style classes have some advanced features lacking in old-style classes, such as super, the new C3 mro, some magical methods, etc.


回答 5

这是一个非常实际的,正确/错误的区别。以下代码的两个版本之间的唯一区别是,在第二个版本中,Personobject继承。除此之外,两个版本相同,但结果不同:

  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>
    >>>

Here’s a very practical, true/false difference. The only difference between the two versions of the following code is that in the second version Person inherits from object. Other than that, the two versions are identical, but with different results:

  1. Old-style classes

    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. New-style classes

    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>
    >>>
    

回答 6

新样式的类继承自objectPython ,并且必须从Python 2.2开始编写(即class Classname(object):而不是class Classname:)。核心更改是统一类型和类,这样做的好处是它允许您从内置类型继承。

阅读descrintro以获得更多详细信息。

New-style classes inherit from object and must be written as such in Python 2.2 onwards (i.e. class Classname(object): instead of class Classname:). The core change is to unify types and classes, and the nice side-effect of this is that it allows you to inherit from built-in types.

Read descrintro for more details.


回答 7

新样式类可以使用super(Foo, self)where Foo是一个类,并且self是一个实例。

super(type[, object-or-type])

返回将方法调用委托给类型的父级或同级类的代理对象。这对于访问已在类中重写的继承方法很有用。搜索顺序与getattr()使用的顺序相同,只是类型本身被跳过。

在Python 3.x中,您可以super()在没有任何参数的类内部简单地使用。

New style classes may use super(Foo, self) where Foo is a class and self is the instance.

super(type[, object-or-type])

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class. The search order is same as that used by getattr() except that the type itself is skipped.

And in Python 3.x you can simply use super() inside a class without any parameters.