Python多重继承:参数传递(**kwargs)和super()

Vzo*_*eet 0 python inheritance multiple-inheritance python-3.x

我试图理解Python多重继承,我有点理解MRO,super()和在MI中传递参数,但是当我阅读下面的例子时,它让我很困惑.

class Contact:
    all_contacts = []

    def __init__(self, name=None, email=None, **kwargs):
        super().__init__(**kwargs)
        self.name = name
        self.email = email
        self.all_contacts.append(self)


class AddressHolder:
    def __init__(self, street=None, city=None, state=None, code=None, **kwargs):
        super().__init__(**kwargs)
        self.street = street
        self.city = city
        self.state = state
        self.code = code


class Friend(Contact, AddressHolder):
    def __init__(self, phone='', **kwargs):
        super().__init__(**kwargs)
        self.phone = phone
Run Code Online (Sandbox Code Playgroud)

现在我无法理解为什么在Contact和AddressHolder类中使用super().我的意思是当我们从父类继承但是Contact&AddressHolder都不从任何其他类继承时使用super().(从技术上讲,他们是继承自的object).这个例子让我对super()的正确使用感到困惑

mgi*_*son 9

所有(新样式)类都具有线性化方法分辨率顺序(MRO).根据继承树,实际计算MRO可能有点令人费解,但它通过相对简单的算法确定性. super获得MRO中下一个课程的委托人.在您的示例中,Friend具有以下MRO:

Friend -> Contact -> AddressHolder -> object
Run Code Online (Sandbox Code Playgroud)

如果你在Friend方法中调用super ,你将得到一个委托给Contact方法的委托人.如果该方法没有调用super,则永远不会调用方法AddressHolder.换句话说,super负责只调用MRO中的下一个方法,而不是MRO中的所有剩余方法.


这一切都很好,因为object有一个完全功能的__init__方法(只要**kwargs在那一点是空的).不幸的是,如果您尝试解决某些自定义方法的调用链,则它不起作用.例如foo.在这种情况下,您希望插入所有基类继承的基类.因为该类是所有类(或至少是基类)继承的基础.该类最终将在MRO结束时进行参数验证1:

class FooProvider:
    def foo(self, **kwargs):
        assert not kwargs  # Make sure all kwargs have been stripped

class Bar(FooProvider):
    def foo(self, x, **kwargs):
        self.x = x
        super().foo(**kwargs)

class Baz(FooProvider):
    def foo(self, y, **kwargs):
        self.y = y
        super().foo(**kwargs)

class Qux(Bar, Baz):
    def foo(self, z, **kwargs):
        self.z = z
        super().foo(**kwargs)     
Run Code Online (Sandbox Code Playgroud)

演示:

>>> q = Qux()
>>> q.foo(x=1, y=2, z=3)
>>> vars(q)
{'z': 3, 'y': 2, 'x': 1}
>>> q.foo(die='invalid')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() missing 1 required positional argument: 'z'
>>> 
>>> q.foo(x=1, y=2, z=3, die='invalid')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/google/home/mgilson/sandbox/super_.py", line 18, in foo
    super().foo(**kwargs)
  File "/usr/local/google/home/mgilson/sandbox/super_.py", line 8, in foo
    super().foo(**kwargs)
  File "/usr/local/google/home/mgilson/sandbox/super_.py", line 13, in foo
    super().foo(**kwargs)
  File "/usr/local/google/home/mgilson/sandbox/super_.py", line 3, in foo
    assert not kwargs  # Make sure all kwargs have been stripped
AssertionError
Run Code Online (Sandbox Code Playgroud)

注意,你仍然可以使用这种方法使用默认参数,这样你就不会松动太多.

1注意,这不是处理这个问题的唯一策略 - 在制作mixins等时可以采取其他方法,但这是目前最强大的方法.