Python 中旧式类和新式类有什么区别?
- 2024-11-22 08:47:00
- admin 原创
- 152
问题描述:
Python 中的旧式类和新式类有什么区别?我什么时候应该使用其中一种?
解决方案 1:
来自新式和古典类:
直到 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 中都是新样式的。
解决方案 2:
声明方面:
新式类继承自object,或者继承自另一个新式类。
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
旧式课程则不然。
class OldStyleClass():
pass
Python 3 注意事项:
Python 3 不支持旧式类,因此上述任何一种形式都会产生新式类。
解决方案 3:
新旧式类之间的重要行为变化
超级添加
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
解决方案 4:
旧式类在属性查找方面仍然略快。这通常并不重要,但在性能敏感的 Python 2.x 代码中可能很有用:
在[3]中:A类:
...: def __init__(self):
...: self.a = '你好'
...:
在[4]中:B类(对象):
...: def __init__(self):
...: self.a = '你好'
...:
在[6]中: aobj = A()
在[7]中:bobj = B()
在 [8]: %timeit aobj.a
10000000 个循环,3 个中最佳:每个循环 78.7 纳秒
在 [10] 中:%timeit bobj.a
10000000 个循环,3 个中最佳:每个循环 86.9 纳秒
解决方案 5:
Guido 撰写了《新式类的内幕故事》,这是一篇关于 Python 中新式类和旧式类的非常棒的文章。
Python 3 只有新式类。即使你写了一个“旧式类”,它也隐式地从中派生出来object
。
新式类具有一些旧式类所缺乏的高级特性,比如super
,新的C3 mro,一些魔法方法等。
解决方案 6:
这是一个非常实用的真/假区别。以下代码的两个版本之间的唯一区别是,在第二个版本中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>
>>>
解决方案 7:
从 Python 2.2 开始,新式类继承自object
并且必须这样写(即class Classname(object):
而不是class Classname:
)。核心变化是统一类型和类,这样做的好处是它允许您从内置类型继承。
阅读描述以了解更多详细信息。
解决方案 8:
新风格的类可能会使用super(Foo, self)
其中Foo
是一个类并且self
是实例。
super(type[, object-or-type])
返回一个代理对象,该代理对象将方法调用委托给类型的父类或兄弟类。这对于访问类中已被重写的继承方法很有用。搜索顺序与 getattr() 使用的顺序相同,只是跳过了类型本身。
在 Python 3.x 中,您可以super()
在类内部简单地使用而不需要任何参数。