简体   繁体   中英

How to minimize redundancy in passing kwargs to multiple super classes?

Given the following classes A , B , and C :

class A:
    def __init__(self, a, aa, aaa):
        self.a = a
        self.aa = aa
        self.aaa = aaa


class B:
    def __init__(self, b, bb, bbb):
        self.b = b
        self.bb = bb
        self.bbb = bbb


class C(A, B):
    def __init__(self, **kwargs):
        super(C, self).__init__(**kwargs)

I want to avoid having to repeat all the superclasses parameters a , aa , aaa , b , bb , bbb , in C definition:

class C(A, B):
    def __init__(self, a, aa, aaa, b, bb, bbb):
        super(C, self).__init__(**kwargs)

and somehow pass A and B kwargs to be resolved in super().__init__ call but this is not possible using the way I described and will result in an error:

>>> c = C(a=1, aa=2, aaa=3, b=4, bb=5, bbb=6)
TypeError: A.__init__() got an unexpected keyword argument 'b'

The correct way of doing so is calling A.__init__(self, **a_kwargs) and B.__init__(self, **b_kwargs) but as I said this creates redundant parameters I'm trying to avoid. Is there a better way to achieve the same thing?

You can do this:

class A:
    def __init__(self, a, aa, aaa, **kwargs):
        self.a = a
        self.aa = aa
        self.aaa = aaa
        super().__init__(**kwargs)

class B:
    def __init__(self, b, bb, bbb, **kwargs):
        self.b = b
        self.bb = bb
        self.bbb = bbb
        super().__init__(**kwargs)

class C(A, B):
    pass

c = C(a=1, aa=2, aaa=3, b=4, bb=5, bbb=6)

Since the C 's MRO is [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] , A will consume it's parameters and pass the rest to B .

  • If you need to pass positional arguments too, You can change it to:
class A:
    def __init__(self, a, aa, aaa, *args, **kwargs):
        self.a = a
        self.aa = aa
        self.aaa = aaa
        super().__init__(*args, **kwargs)

class B:
    def __init__(self, b, bb, bbb, *args, **kwargs):
        self.b = b
        self.bb = bb
        self.bbb = bbb
        super().__init__(*args, **kwargs)

class C(A, B):
    pass

You could use built-in signature(callable) function to obtain expected keyword arguments for each __init__ and pass only those that the function expects like so. Whether this is good design, that is a matter of another discussion.

from inspect import signature

class A:
    def __init__(self, a, aa, aaa):
        self.a = a
        self.aa = aa
        self.aaa = aaa
        print(f"A obj: {a=}, {aa=}, {aaa=}")

class B:
    def __init__(self, b, bb, bbb):
        self.b = b
        self.bb = bb
        self.bbb = bbb
        print(f"B obj: {b=}, {bb=}, {bbb=}")

class C(A, B):
    def __init__(self, **kwargs):
        A_params = {k:v for k, v in kwargs.items() if k in signature(A.__init__).parameters.keys()}
        A.__init__(self, **A_params)
        B_params = {k:v for k, v in kwargs.items() if k in signature(B.__init__).parameters.keys()}
        B.__init__(self, **B_params)

c = C(a=1, aa=2, aaa=3, b=4, bb=5, bbb=6)

Output:

A obj: a=1, aa=2, aaa=3
B obj: b=4, bb=5, bbb=6

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM