Python 多重继承:参数传递 (**kwargs) 和 super()
Python Multiple Inheritance: Argument passing (**kwargs) and super()
我正在尝试理解 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() class。我的意思是当我们从父 class 继承时使用 super() 但 Contact 和 AddressHolder 都没有从任何其他 class 继承。 (从技术上讲,它们继承自 object
)。这个例子让我对 super()
的正确使用感到困惑
所有(新样式)classes 都具有线性化方法解析顺序 (MRO)。根据继承树,实际计算出 MRO 可能有点令人费解,但它是通过 relatively simple algorithm 确定的。您还可以通过 class 的 __mro__
属性检查 MRO。
super
获得 MRO 中下一个 class 的委托人。在您的示例中,Friend
具有以下 MRO:
Friend -> Contact -> AddressHolder -> object
如果您在 Friend
的方法之一中调用 super,您将得到一个委托给 Contact
的方法的委托者。如果该方法不调用 super
,您将永远不会调用 AddressHolder
上的方法。也就是说,super
只负责调用MRO中的下一个方法,而不是ALLMRO中剩余的方法。
(如果您在 Friend
的方法之一中调用 super
并且 Contact
没有自己的该方法的实现,那么 super
将委托到 AddressHolder
,或者 class 在 MRO 中有该方法的下一个实现。)
这很好,因为 object
有一个完全可用的 __init__
方法(只要 **kwargs
在那个时候是空的)。不幸的是,如果您试图解析某些自定义方法的调用链,它就不起作用。例如foo
。在这种情况下,您想要插入一个所有基 class 继承自的基 class。因为 class 是所有 classes(或至少是 classes)继承的基础。那class会在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 等时,您还可以采用其他方法。但这是迄今为止最稳健的方法。
我正在尝试理解 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() class。我的意思是当我们从父 class 继承时使用 super() 但 Contact 和 AddressHolder 都没有从任何其他 class 继承。 (从技术上讲,它们继承自 object
)。这个例子让我对 super()
所有(新样式)classes 都具有线性化方法解析顺序 (MRO)。根据继承树,实际计算出 MRO 可能有点令人费解,但它是通过 relatively simple algorithm 确定的。您还可以通过 class 的 __mro__
属性检查 MRO。
super
获得 MRO 中下一个 class 的委托人。在您的示例中,Friend
具有以下 MRO:
Friend -> Contact -> AddressHolder -> object
如果您在 Friend
的方法之一中调用 super,您将得到一个委托给 Contact
的方法的委托者。如果该方法不调用 super
,您将永远不会调用 AddressHolder
上的方法。也就是说,super
只负责调用MRO中的下一个方法,而不是ALLMRO中剩余的方法。
(如果您在 Friend
的方法之一中调用 super
并且 Contact
没有自己的该方法的实现,那么 super
将委托到 AddressHolder
,或者 class 在 MRO 中有该方法的下一个实现。)
这很好,因为 object
有一个完全可用的 __init__
方法(只要 **kwargs
在那个时候是空的)。不幸的是,如果您试图解析某些自定义方法的调用链,它就不起作用。例如foo
。在这种情况下,您想要插入一个所有基 class 继承自的基 class。因为 class 是所有 classes(或至少是 classes)继承的基础。那class会在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 等时,您还可以采用其他方法。但这是迄今为止最稳健的方法。