【问题标题】:Python Multiple Inheritance: Argument passing (**kwargs) and super()Python 多重继承:参数传递 (**kwargs) 和 super()
【发布时间】:2015-05-04 10:34:33
【问题描述】:

我正在尝试理解 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

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

【问题讨论】:

  • afaik 它调用了 MRO 堆栈中的下一个项目 ....

标签: python inheritance python-3.x multiple-inheritance


【解决方案1】:

所有(新型)类都有一个线性化的方法解析顺序 (MRO)。根据继承树,实际上找出 MRO 可能有点令人费解,但它是通过relatively simple algorithm 确定的。也可以通过类的__mro__属性查看MRO。

super 获得 MRO 中下一个类的委托人。在您的示例中,Friend 具有以下 MRO:

Friend -> Contact -> AddressHolder -> object

如果您在 Friend 的方法之一中调用 super,您将获得一个委托给 Contact 的方法的委托人。如果该方法不调用super,您将永远不会调用AddressHolder 上的方法。换句话说,super 只负责调用 MRO 中的下一个方法,而不是ALL MRO 中的其余方法。

(如果您在Friend 的方法之一中调用super 并且Contact 没有自己的该方法实现,那么super 将委托给AddressHolder,或者任何具有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)     

演示:

>>> 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

注意,您仍然可以使用这种方法使用默认参数,这样您就不会在那里损失太多。

1注意,这不是解决这个问题的唯一策略——在制作 mixin 等时,你还可以采用其他方法,但这是迄今为止最常用的方法稳健的方法。

【讨论】:

  • 非常感谢@mgilson 提供了这么好的解释。现在很有意义。
猜你喜欢
  • 2018-11-06
  • 2016-02-25
  • 2016-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-25
  • 1970-01-01
相关资源
最近更新 更多