Answers:
从新式和经典类:
直到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中的新型样式。
super()
不适用于旧类。就像该文章所说的那样,更不用说了一些基本修复程序,例如MRO和特殊方法,这绝对是使用它的充分理由。
声明方式:
新样式类继承自object或另一个新类。
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不继承任何父类”。这是相当相似not is
和is not
新旧样式类之间的重要行为更改
它在其他答案中也提到过,但是这里有一个具体示例,说明了经典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代码中可能有用:
在[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最佳
%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撰写了有关New-Style Classes的The Inside Story,这是一篇有关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>
>>>
新型班
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__()
,始终会调用它,并且它总是构造一个新对象,然后将其抛出。在这种情况下,a if
优于.setdefault()
。
__new__
实际上,这不是老式类的东西,它也不会在实例构造中使用(它只是一个看起来很特殊的随机名称,例如define __spam__
)。因此,构造旧类仅调用__init__
,而构造新类__new__
(按名称推销到单例实例)来构造和__init__
初始化它。
新样式的类继承自object
Python ,并且必须从Python 2.2开始编写(即class Classname(object):
而不是class Classname:
)。核心更改是统一类型和类,这样做的好处是它允许您从内置类型继承。
阅读descrintro以获得更多详细信息。
type(x)
。如果我不对内置类型进行子类化,那么新样式类似乎没有任何优势。有一个缺点,那就是的额外输入(object)
。