1074

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

4

8 回答 8

600

新式和经典类

直到 Python 2.1,旧式类是用户唯一可用的风格。

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

这反映了一个事实,即所有老式实例,独立于它们的类,都是用一个称为实例的内置类型实现的。

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

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

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

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

出于兼容性原因,默认情况下类仍然是老式的

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

除了返回的类型外,新式类的行为在许多重要细节上与旧式类的行为不同。

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

Python 3 只有新式类

无论您是否从子类object化,类都是 Python 3 中的新样式。

于 2008-09-10T18:02:43.743 回答
326

声明方面:

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

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

旧式课程没有。

class OldStyleClass():
    pass

Python 3 注意:

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

于 2009-07-30T01:21:59.097 回答
237

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

  • 超级添加
  • 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
于 2013-11-13T09:36:59.783 回答
41

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

[3] 中:A 类:
   ...:定义__init__(自我):
   ...: self.a = '你好'
   ...:

在 [4] 中:B 类(对象):
   ...:定义__init__(自我):
   ...: self.a = '你好'
   ...:

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

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

在 [10] 中:%timeit bobj.a
10000000 次循环,3 次中的最佳:每个循环 86.9 ns
于 2010-07-12T11:26:52.043 回答
38

Guido 写了 The Inside Story on New-Style Classes,这是一篇关于 Python 中新式和旧式类的非常棒的文章。

Python 3 只有新式类。即使您编写了一个“旧式类”,它也隐式地派生自object.

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

于 2013-04-24T13:41:26.200 回答
25

这是一个非常实用的真/假区别。以下代码的两个版本之间的唯一区别是在第二个版本中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>
    >>>
    
于 2013-10-09T13:40:29.397 回答
10

新式类继承自objectPython 2.2 及以后的版本,并且必须这样编写(即class Classname(object):,而不是class Classname:)。核心变化是统一类型和类,这样做的好处是它允许您从内置类型继承。

阅读说明以获取更多详细信息。

于 2008-10-28T09:54:30.437 回答
8

新样式类可以使用super(Foo, self)where Foois a class and selfis the instance。

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

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

而在 Python 3.x 中,您可以简单地super()在类中使用而无需任何参数。

于 2013-04-30T08:28:52.780 回答