Python中旧式类和新式类有什么区别?需要在何时使用它们之一?
来自新式和经典类:
直到Python 2.1,旧式类是用户唯一可用的类型。旧式类的概念与类型的概念无关:如果x是旧式类的实例,则x.__class__指定了x的类,但type(x)始终为。这反映了所有旧式实例的事实,独立于它们的类,都是使用称为实例的单个内置类型实现的。声明方式:
新式类继承自object,或者从另一个新式类继承。
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
旧式类不支持。
class OldStyleClass():
pass
Python 3 注意:
Python 3 不支持旧式类,因此上述任一形式都将产生新式类。
object
。 - aaronasterlingclass AnotherOldStyleClass: pass
- Ankur Agarwalclass OldStyleOptional(object): pass
- cowlinator旧式类和新式类之间的重要行为变化
其他答案中已经提到过,但这里给出一个具体的例子来说明经典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
旧式类仍然在属性查找方面略快。这通常不重要,但在性能敏感的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次循环,3个最佳:每次66.1纳秒
%timeit bobj.a
10000000次循环,3个最佳:每次53.9纳秒
- Benedikt WaldvogelGuido写了一篇关于Python中新旧类的非常好的文章,标题为The Inside Story on New-Style Classes。
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>
>>>
新式类
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
方法(在任何字典中定义)接受两个参数:键和值。如果该键在字典中,则返回其对应的值。如果不在字典中,则先将该键的值设置为第二个参数,然后返回该值。 - ychaouche__new __()
,并始终构造新对象,然后将其丢弃。在这种情况下,使用“if”比“.setdefault()”更可取。 - Amit Upadhyay__new__
实际上并不存在,它不会在实例构造中使用(它只是一个看起来很特别的随机名称,就像定义__spam__
一样)。因此,构造旧式类仅调用__init__
,而新式构造则调用__new__
(通过名称合并为单例实例)进行构造,并调用__init__
进行初始化。 - ShadowRangerobject
,在Python 2.2及以后的版本中必须这样编写(即class Classname(object):
而不是class Classname:
)。核心变化是统一类型和类,这样做的好处是可以从内置类型中继承。
阅读descrintro以获取更多详细信息。
super(Foo, self)
,其中Foo
是一个类,self
是实例。
而在Python 3.x中,你可以简单地在一个类中使用
super(type[, object-or-type])
返回一个代理对象,将方法调用委托给类型的父类或兄弟类。这对于访问在类中被覆盖的继承方法非常有用。搜索顺序与getattr()使用的顺序相同,只是跳过了类型本身。
super()
而不需要任何参数。
type(x)
。 如果我不是在子类化内置类型,则我看不到使用新式类的任何优势。它有一个劣势,就是额外输入(object)
。 - recursivesuper()
这样的某些特性在旧式类上不起作用。更何况,正如那篇文章所说,存在基本修复方式,比如 MRO 和特殊方法,这是使用它的绝佳理由。 - John Doe