Python中的旧样式类和新样式类有什么区别?我什么时候应该使用其中一种?
来自 New-style and classic classes:
直到 Python 2.1,旧式类是用户唯一可用的风格。 (old-style) class 的概念与 type 的概念无关:如果 x 是 old-style class 的一个实例,则 x.__class__ 指定 x 的类,但 type(x) 总是
声明方面:
新式类继承自对象,或从另一个新式类。
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
旧式课程没有。
class OldStyleClass():
pass
Python 3 注意:
Python 3 不支持旧样式类,因此上述任何一种形式都会产生新样式类。
object
。
class AnotherOldStyleClass: pass
class A: pass
和 class A(): pass
是严格等价的。第一个表示 "A 不继承任何父类",第二个表示 "A inherits of no parent class" 。这与 not is
和 is not
非常相似
新旧样式类之间的重要行为变化
超级添加
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
New-style classes MRO 在单个英文句子中合成起来更加复杂。详细解释here。它的属性之一是基类仅在其所有派生类都被搜索后才被搜索。它们具有显示搜索顺序的 __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 代码中可能很有用:
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
%timeit aobj.a
10000000 loops, best of 3: 66.1 ns per loop
%timeit bobj.a
10000000 loops, best of 3: 53.9 ns per loop
Guido 撰写了The Inside Story on New-Style Classes,这是一篇关于 Python 中新式和旧式类的非常棒的文章。
Python 3 只有新式类。即使您编写了一个“旧式类”,它也隐式地从 object
派生。
新式类具有旧式类所缺乏的一些高级特性,如super
、新的C3 mro、一些神奇的方法等。
这是一个非常实用的真/假区别。以下代码的两个版本之间的唯一区别是,在第二个版本中,Person 继承自 object。除此之外,这两个版本是相同的,但结果不同:
旧式类 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> >>> New -style 类 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> >>>
_names_cache
是一个字典,用于缓存(存储以供将来检索)您传递给 Person.__new__
的每个名称。 setdefault 方法(在任何字典中定义)有两个参数:一个键和一个值。如果键在字典中,它将返回其值。如果它不在字典中,它将首先将其设置为作为第二个参数传递的值,然后返回它。
__new__()
总是被调用,它总是构造一个新对象,然后抛出它。在这种情况下,if
优于 .setdefault()
。
__new__
实际上不是用于旧式类的东西,它不会用于实例构造(它只是一个看起来特别的随机名称,例如定义 __spam__
)。所以构造旧式类只调用 __init__
,而新式构造调用 __new__
(通过名称合并为单例实例)来构造,并调用 __init__
来初始化它。
新式类继承自 object
并且必须在 Python 2.2 及更高版本中这样编写(即 class Classname(object):
而不是 class Classname:
)。核心变化是统一类型和类,这样做的好处是它允许您从内置类型继承。
阅读descrintro了解更多详情。
新样式类可以使用 super(Foo, self)
,其中 Foo
是类,self
是实例。
super(type[, object-or-type]) 返回一个代理对象,它将方法调用委托给类型的父类或同级类。这对于访问已在类中重写的继承方法很有用。搜索顺序与 getattr() 使用的相同,只是跳过了类型本身。
在 Python 3.x 中,您可以简单地在类中使用 super()
而无需任何参数。
type(x)
。如果我没有对内置类型进行子类化,那么我似乎看不到新式类的任何优势。有一个缺点,那就是(object)
的额外输入。super()
等某些功能不适用于旧式课程。更不用说,正如那篇文章所说,有基本修复,如 MRO 和特殊方法,这不仅仅是使用它的好理由。