在subcalssing dict和其他东西时没有调用__init__

时间:2015-05-18 07:52:48

标签: python multiple-inheritance built-in

请考虑以下代码:

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 LockableDict(Lockable, dict):

让它发挥作用。为什么呢?

1 个答案:

答案 0 :(得分:4)

您的误解在此评论中很清楚:

  

我不会覆盖__init__中的LockableDict,因此它应生成__init__自动调用基类&#39}。 __init__ s,不应该吗?

没有!

首先,没有任何东西自动生成;方法解析在呼叫时发生。

在通话时,Python不会调用每个基类的__init__,它只会调用它找到的第一个 * < / SUP>

这就是super存在的原因。如果你没有在覆盖中显式调用super,那么基类甚至兄弟类都不会调用它们的实现。而dict.__init__并未调用其super

你的Lockable.__init__也没有。因此,这意味着撤消订单会确保调用Lockable.__init__ ...但dict.__init__现在不会被调用

那么,你真的想做什么?好吧,Python的MRO算法旨在为合作类的层次结构提供最大的灵活性,最后抛出任何非合作类。所以,你可以这样做:

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

另请注意,这允许您通过所有合作类在链中传递初始化参数,然后使用您知道它需要的参数调用不友好的类__init__

在少数情况下,你必须先安排一个不友好的课程。 ** 在这种情况下,你必须明确地打电话给他们:

class LockableDict(dict, Lockable):
    def __init__(self):
        dict.__init__(self)
        Lockable.__init__(self)

但幸运的是,这并不经常出现。

*在标准method resolution order中,这比您预期的要复杂一些。当您致电super以找到&#34; next&#34;时,会应用相同的MRO规则。 class - 可以是基类,兄弟,甚至是子类的兄弟。您可能会发现C3 linearization上的维基百科文章更加平易近人。

**特别是对于内置课程,因为他们在某些方面并不特别值得进入这里。然后,许多内置类实际上并没有在__init__中执行任何操作,而是在__new__构造函数中进行所有初始化。