Reputation: 48466
I want to add keyword arguments to a derived class, but can't figure out how to go about it. Trying the obvious
class ClassA(some.package.Class):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class ClassB(ClassA):
def __init__(self, *args, a='A', b='B', c='C', **kwargs):
super().__init__(*args, **kwargs)
self.a=a
self.b=b
self.c=c
fails because I can't list parameters like that for ClassB
's __init__
. And
class ClassB(ClassA):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.a=a
self.b=b
self.c=c
of course doesn't work because the new keywords aren't specified.
How do I add keyword arguments to the __init__
for a derived class?
Upvotes: 11
Views: 5717
Reputation: 67987
Try doing it like this:
class ClassA:
def __init__(self, *args, **kwargs):
pass
class ClassB(ClassA):
def __init__(self, *args, **kwargs):
self.a = kwargs.pop('a', 'A')
self.b = kwargs.pop('b', 'B')
self.c = kwargs.pop('c', 'C')
super().__init__(*args, **kwargs)
Effectively you add the keyword arguments a
, b
and c
to ClassB
, while passing on other keyword arguments to ClassA
.
Upvotes: 10
Reputation: 28253
def __init__(self, a='A', b='B', c='C', *args, **kwargs):
Modifying the OP's code with the child class's constructor having the above signature:
class BaseClass(object):
def __init__(self, *args, **kwargs):
self.args = args
for k, v in kwargs.items():
setattr(self, k, v)
class ClassA(BaseClass):
def __init__(self, *args, **kwargs):
super(ClassA, self).__init__(*args, **kwargs)
class ClassB(ClassA):
def __init__(self, a='A', b='B', c='C', *args, **kwargs):
self.a = a
self.b = b
self.c = c
super(ClassA, self).__init__(*args, **kwargs)
A = ClassA('hello', 'world', myname='hal',myemail='[email protected]')
B = ClassB('hello', 'world', myname='hal', myemail='[email protected]')
print("CLASS A:", A.__dict__)
print("CLASS B:", B.__dict__)
# yields the following:
CLASS A: {'args': ('hello', 'world'), 'myname': 'hal', 'myemail': '[email protected]'}
CLASS B: {'args': (), 'myname': 'hal', 'a': 'hello', 'myemail': '[email protected]', 'c': 'C', 'b': 'world'}
The code will not throw an exception, however, a, b & c are now positionally the first 3 arguments passed to the method (ignoring self
) , as seen from the two object's dicts.
Accessing the attributes a, b & c shows this other effect
>>> B.a # expect to see 'A'
'hello'
>>> B.b # similarly
'world'
>>> B.c # but this retails the default value
'C'
AFAIK, you can't add new keyword only arguments to the method signature. Please correct me if I'm wrong.
However, both @aknuds1 and @Oleh Prypin provided solutions that effectively add new keyword arguments to the child class. Oleh's solution is a bit more clever, but I find aknuds1's version easier to understand.
kwargs
dict, coalesced to the default value setattr
Upvotes: 2
Reputation: 43166
All you need do is rearrange the arguments.
def __init__(self, a='A', b='B', c='C', *args, **kwargs):
Upvotes: 4
Reputation: 34116
Use Python 3.x, which makes keyword-only arguments valid.
Or use this workaround...
class ClassB(ClassA):
def __init__(self, *args, **kwargs):
for arg, default in [('a', 'A'), ('b', 'B'), ('c', 'C')]:
setattr(self, arg, kwargs.pop(arg, default))
super(ClassA, self).__init__(*args, **kwargs)
Upvotes: 0