How do I add keyword arguments to a derived class's constructor in Python?

淺唱寂寞╮ 提交于 2019-12-04 07:23:42

Try doing it like this:

class ClassA(object):
    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(ClassB, self).__init__(*args, **kwargs)

Effectively you add the keyword arguments a, b and c to ClassB, while passing on other keyword arguments to ClassA.

All you need do is rearrange the arguments.

def __init__(self, a='A', b='B', c='C', *args, **kwargs):

Effect of 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='hal@hal.hal')
B = ClassB('hello', 'world', myname='hal', myemail='hal@hal.hal')

print("CLASS A:", A.__dict__)
print("CLASS B:", B.__dict__)
# yields the following:
CLASS A: {'args': ('hello', 'world'), 'myname': 'hal', 'myemail': 'hal@hal.hal'}
CLASS B: {'args': (), 'myname': 'hal', 'a': 'hello', 'myemail': 'hal@hal.hal', '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.

  • pop elements from the kwargs dict, coalesced to the default value
  • assign to the instance attribute using setattr
  • call parent constructor with args, kwargs

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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!