为内置类型的多重继承子类构造函数添加可选参数?
我对多重继承的理解还不够深刻。我想创建一个父类,这个父类的 __init__
方法可以接受一个可选的命名参数,同时它的子类也要继承一些内置类型。但可惜的是,我似乎不知道该怎么做:
>>> class Super(object):
name = None
def __init__(self, *args, name=None, **kwargs):
self.name = name
super().__init__(self, *args, **kwargs)
>>> class Sub(Super, int):
pass
>>> Sub(5)
5
>>> Sub(5, name="Foo")
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
Sub(5, name="Foo")
TypeError: 'name' is an invalid keyword argument for this function
(我也试过不使用 super()
调用,但结果还是一样。)
也许有更懂多重继承的人可以给我一些指点?
更新:
这是我最终得到的解决方案,基于 Alex的回答。
这个方法还有点不太规范(因为在构造过程中改变了 __new__
的签名),但只要父类在子类的MRO(方法解析顺序)中出现在内置类型之前,这个方法就能正常工作。而且这样定义 __new__
让我可以创建与不同内置类型兼容的子类,而不需要为每个子类单独添加 __new__
方法。
>>> class Super(object):
name = None
def __new__(cls, *args, name=None, **kwargs):
inner = super().__new__(cls, *args, **kwargs)
inner.name = name
return inner
>>> class Sub(Super, int):
pass
>>> Sub(5)
5
>>> Sub(5, name="Foo")
5
>>> _.name
'Foo'
1 个回答
你不能随便给一个父类(也就是当前类型的“上一个类型”)传递任意的参数(无论是位置参数还是命名参数)。大多数类型和类都不接受随意的参数,这样做是有很好的理由的。引用《Python之禅》中的一句话:
Errors should never pass silently.
Unless explicitly silenced.
在大多数情况下,调用比如 int(name='booga')
当然会出错。
如果你希望你那个奇怪命名的 class Super
能够“传递”任意参数,你还得确保它后面所有作为基类的类都能处理这些参数。比如说,int
可以接受一个参数(或者正好两个:一个字符串和一个进制),所以如果你特别希望 class Sub
能够同时继承这个能传递参数的 Super
和 int
,你就得考虑这个问题,例如:
class Int(int):
def __new__(cls, *a, **k):
return int.__new__(Int, a[0] if a else 0)
注意,你必须重写 __new__
,而不是 __init__
(如果你也重写后者也没关系,但其实没什么必要):int
是不可变的,所以值必须在 __new__
时就设置好。
现在,像这样的代码:
>>> class X(Super, Int): pass
...
>>> X(23, za='zo')
23
>>>
就能正常工作了。但要注意,X
必须从 Int
(我们重写过的 int
)继承,而不是直接从 int
继承,因为后者有一个严格的 __new__
方法!