Adding optional parameters to the constructors of multiply-inheriting subclasses of built-in types?
My multiple-inheritance-fu is not strong. I am trying to create a superclass whose __init__
takes an optional named parameter and subclasses of it which also inherit from built-in types. Sadly, I appear to have no idea how to make this work:
>>> class Super(object):
name = None
def 开发者_StackOverflow社区__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
(I've also tried it without the super()
call, but the result was the same.)
Perhaps someone with better knowledge of multiple inheritance can point me in the right direction?
Update:
Here is the solution I ended up with, based on Alex's answer.
It's still a little hacky (by changining __new__
's signature partway through construction), but it will work as long as the superclass appears in the subclasses' MROs before the built-in type and defining __new__
this way allows me to make subclasses which work with different built-in types without adding a separate __new__
to each one.
>>> 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'
You just cannot pass arbitrary parameters (whether positional or named ones) to an equally arbitrary superclass (i.e., "immediately previous type in the mro
of whatever the current leaf type is") -- most types and classes just don't accept arbitrary parameters, for excellent reasons too -- quoting from the middle of the Zen of Python
,
Errors should never pass silently.
Unless explicitly silenced.
and in most cases, calling (e.g.) int(name='booga')
would of course be an error.
If you want you weirdly-named class Super
to be able to "pass on" arbitrary parameters, you must also ensure that all classes ever used as bases after it can handle that -- so, for example, int
can be called with one parameter (or exactly two: a string and a base), so, if it's absolutely crucial to you that class Sub
can multiply inherit from the buck-passing Super
and int, you have to field that, e.g.:
class Int(int):
def __new__(cls, *a, **k):
return int.__new__(Int, a[0] if a else 0)
Note that you must override __new__
, not __init__
(it does no harm if you also override the latter, but it's irrelevant anyway): int
is immutable so the value has to be set at __new__
time.
Now, things like
>>> class X(Super, Int): pass
...
>>> X(23, za='zo')
23
>>>
work. But note that X
must subclass from Int
(our __new__
-sanitizing version of int
), not from int
itself, which, quite properly, has an unforgiving __new__
!-)
精彩评论