__init__ 子类化 dict 和其他东西时不调用
__init__ not called when subcalssing dict and something else
考虑以下代码:
class Lockable(object):
def __init__(self):
self._lock = None
def is_locked(self):
return self._lock is None
class LockableDict(dict, Lockable):
pass
现在:
In [2]: l = example.LockableDict(a=1, b=2, c=3)
In [3]: l.is_locked()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-4344e3948b9c> in <module>()
----> 1 l.is_locked()
/home/sven/utils/example.py in is_locked(self)
8
9 def is_locked(self):
---> 10 return self._lock is None
AttributeError: 'LockableDict' object has no attribute '_lock'
看起来 Lockable.__init__
根本没有被调用。是这样吗?为什么?
为了让事情更有趣,事实证明,像这样更改 LockableDict class header 就足够了:
class LockableDict(Lockable, dict):
让它发挥作用。为什么?
从这条评论可以清楚地看出你的误解:
I don't override __init__
in LockableDict
, so it should generate an __init__
making automatic calls to base classes' __init__
s, shouldn't it?
没有!
首先,不会自动生成任何内容;方法解析发生在调用时。
并且在调用时,Python不会调用每个基class的__init__
,它只会调用第一个它找到了一个。*
这就是 super
存在的原因。如果您没有在覆盖中显式调用 super
,则基 classes 甚至兄弟 classes 都不会调用它们的实现。 dict.__init__
不调用它的 super
.
你的 Lockable.__init__
也没有。所以,这意味着颠倒顺序可以确保 Lockable.__init__
被调用……但是 dict.__init__
现在 不会 被调用。
那么,你到底想做什么?那么,Python 的 MRO 算法旨在为 合作 classes 的层次结构提供最大的灵活性,抛出任何非合作 classes在最后。所以,你可以这样做:
class Lockable(object):
def __init__(self):
super().__init__() # super(Lockable, self).__init__() for 2.x
self._lock = None
def is_locked(self):
return self._lock is None
class LockableDict(Lockable, dict):
pass
另请注意,这允许您通过所有合作的 classes 沿链传递初始化参数,然后只需使用参数调用不友好的 class 的 __init__
你知道它需要。
在某些情况下,您必须先放置一个不友好的 class。** 在这种情况下,您必须明确地调用它们:
class LockableDict(dict, Lockable):
def __init__(self):
dict.__init__(self)
Lockable.__init__(self)
但幸运的是,这种情况并不经常出现。
* 在标准中method resolution order, which is slightly more complicated than you'd expect. The same MRO rule is applied when you call super
to find the "next" class—which may be a base class, or a sibling, or even a sibling of a subclass. You may find the Wikipedia article on C3 linearization更平易近人。
** 特别是对于内置的 classes,因为它们在某些方面很特殊,不值得在这里深入探讨。再一次,许多内置 classes 实际上并没有在它们的 __init__
中做任何事情,而是在 __new__
构造函数中进行所有初始化。
考虑以下代码:
class Lockable(object):
def __init__(self):
self._lock = None
def is_locked(self):
return self._lock is None
class LockableDict(dict, Lockable):
pass
现在:
In [2]: l = example.LockableDict(a=1, b=2, c=3)
In [3]: l.is_locked()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-4344e3948b9c> in <module>()
----> 1 l.is_locked()
/home/sven/utils/example.py in is_locked(self)
8
9 def is_locked(self):
---> 10 return self._lock is None
AttributeError: 'LockableDict' object has no attribute '_lock'
看起来 Lockable.__init__
根本没有被调用。是这样吗?为什么?
为了让事情更有趣,事实证明,像这样更改 LockableDict class header 就足够了:
class LockableDict(Lockable, dict):
让它发挥作用。为什么?
从这条评论可以清楚地看出你的误解:
I don't override
__init__
inLockableDict
, so it should generate an__init__
making automatic calls to base classes'__init__
s, shouldn't it?
没有!
首先,不会自动生成任何内容;方法解析发生在调用时。
并且在调用时,Python不会调用每个基class的__init__
,它只会调用第一个它找到了一个。*
这就是 super
存在的原因。如果您没有在覆盖中显式调用 super
,则基 classes 甚至兄弟 classes 都不会调用它们的实现。 dict.__init__
不调用它的 super
.
你的 Lockable.__init__
也没有。所以,这意味着颠倒顺序可以确保 Lockable.__init__
被调用……但是 dict.__init__
现在 不会 被调用。
那么,你到底想做什么?那么,Python 的 MRO 算法旨在为 合作 classes 的层次结构提供最大的灵活性,抛出任何非合作 classes在最后。所以,你可以这样做:
class Lockable(object):
def __init__(self):
super().__init__() # super(Lockable, self).__init__() for 2.x
self._lock = None
def is_locked(self):
return self._lock is None
class LockableDict(Lockable, dict):
pass
另请注意,这允许您通过所有合作的 classes 沿链传递初始化参数,然后只需使用参数调用不友好的 class 的 __init__
你知道它需要。
在某些情况下,您必须先放置一个不友好的 class。** 在这种情况下,您必须明确地调用它们:
class LockableDict(dict, Lockable):
def __init__(self):
dict.__init__(self)
Lockable.__init__(self)
但幸运的是,这种情况并不经常出现。
* 在标准中method resolution order, which is slightly more complicated than you'd expect. The same MRO rule is applied when you call super
to find the "next" class—which may be a base class, or a sibling, or even a sibling of a subclass. You may find the Wikipedia article on C3 linearization更平易近人。
** 特别是对于内置的 classes,因为它们在某些方面很特殊,不值得在这里深入探讨。再一次,许多内置 classes 实际上并没有在它们的 __init__
中做任何事情,而是在 __new__
构造函数中进行所有初始化。