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

时间:2008-09-10 18:01:27

标签: python class oop types new-style-class

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

9 个答案:

答案 0 :(得分:519)

来自http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes

  

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

     

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

     

这反映了所有旧式实例的独立性   他们的类,用一个内置类型实现,称为   实例

     

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

     

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

     

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

     

它还有许多直接的好处,比如能力   子类大多数内置类型,或引入“描述符”,   它启用了计算属性。

     

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

     

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

     

新式课程的行为与旧式课程的行为不同   除了什么类型之外,还有一些重要的细节类   回报。

     

其中一些变化是新对象模型的基础,例如   调用特殊方法的方式。其他人是“修复”,不能   之前要实现兼容性问题,比如方法   多重继承时的解析顺序。

     

Python 3只有新式的类

     

无论你是否从object继承,类都是新式的   在Python 3中。

答案 1 :(得分:288)

<强>宣言明智:

新式类继承自object或其他新式类。

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

旧式课没有。

class OldStyleClass():
    pass

答案 2 :(得分:211)

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

  • super已添加
  • MRO已更改(如下所述)
  • descriptors已添加
  • 除非派生自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在单个英语句子中合成更复杂。详细解释here。它的一个属性是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

答案 3 :(得分:36)

对于属性查找,旧样式类仍然稍微快一些。这通常不重要,但在性能敏感的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

答案 4 :(得分:32)

Guido编写了The Inside Story on New-Style Classes,这是一篇关于Python中新风格和旧式类的精彩文章。

Python 3只有新式的类,即使你写了一个“旧式类”,它也是隐式派生自object

新式课程有一些缺乏旧式课程的高级功能,例如super和新的C3 mro,一些神奇的方法等。

答案 5 :(得分:21)

这是一个非常实用的,真/假的差异。以下代码的两个版本之间的唯一区别是,在第二个版本中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>
>>>

答案 6 :(得分:7)

新式类继承自object,必须在Python 2.2之后编写(即class Classname(object):而不是class Classname:)。核心变化是统一类型和类,其中很好的副作用是它允许你从内置类型继承。

阅读descrintro了解详情。

答案 7 :(得分:5)

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

  

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

     

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

在Python 3.x中,您只需在没有参数的类中使用super()

答案 8 :(得分:4)

或者更确切地说,你应该总是使用新式的类,除非你有代码需要使用早于2.2的Python版本。