Home > Back-end >  Python mixins : how to deal with *args, **kwargs when calling super()?
Python mixins : how to deal with *args, **kwargs when calling super()?

Time:11-08

In the following example I show some mixins that may or may not use *args or **kwargs:

class AMixin():
    def __init__(self, **kwargs):
        print("AMixin")
        self.a = 'a'   str(kwargs.get('a', ''))
        
class BMixin():
    def __init__(self, **kwargs):
        print("BMixin")        
        self.b = 'b'   str(kwargs.get('b', ''))
                
class ABMixin(AMixin, BMixin):
    def __init__(self, **kwargs):
        print("ABMixin")
        super().__init__(**kwargs)
    
class A(AMixin):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
    
class B(BMixin):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
    
class AB(ABMixin):
    def __init__(self, **kwargs):
        print("AB")
        super().__init__(**kwargs)
    
AB(a='a', b='b')

Here I encounter an issue because the last mixin is calling its parent which doesn't exist:

Cell In [4], line 9, in BMixin.__init__(self, *args, **kwargs)
      8 def __init__(self, *args, **kwargs):
----> 9     super().__init__(*args, **kwargs)
     10     print("BMixin")
     11     self.b = 'b'   str(kwargs.get('b', ''))

TypeError: object.__init__() takes exactly one argument 
           (the instance to initialize)

How should I modify this example to allow any combination of mixins, and inherit mixins in any order?

One possible UGLY solution is to add a dummy end mixin:

class EndMixin:
    def __init__(self, **kwargs):
        ...
        
class AMixin(EndMixin):
    def __init__(self, **kwargs):
        print("AMixin")
        self.a = 'a'   str(kwargs.get('a', ''))
        
class BMixin(EndMixin):
    def __init__(self, **kwargs):
        print("BMixin")        
        self.b = 'b'   str(kwargs.get('b', ''))
               

CodePudding user response:

You can make your mixins inherit from a base-class that stops the super call to object.

class BaseMixin:
    def __init__(self, *args, **kwargs):
        pass


class AMixin(BaseMixin):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print("AMixin")
        self.a = 'a'   str(kwargs.get('a', ''))


class BMixin(BaseMixin):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print("BMixin")
        self.b = 'b'   str(kwargs.get('b', ''))


class ABMixin(AMixin, BMixin):
    ...


class A(AMixin):
    ...


class B(BMixin):
    ...


class AB(ABMixin):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


obj = AB(a='a', b='b')
print(obj.b)
print(obj.a)

CodePudding user response:

It looks like each mix-in should take ownership of the argument it operates on. Declare the parameter explicitly (keyword-only if you like), and leave **kwargs to handle the keyword arguments your mix-in doesn't know about.

class AMixin:
    def __init__(self, *args, a='', **kwargs):
        super().__init__(*args, **kwargs)
        print("AMixin")
        self.a = 'a'   str(a)
        
class BMixin:
    def __init__(self, *args, b='',  **kwargs):
        super().__init__(*args, **kwargs)
        print("BMixin")
        self.b = 'b'   str(b)


class A(AMixin):
    pass


class B(BMixin):
    pass


class AB(ABMixin):
    def __init__(self, **kwargs):
        print("AB")
        super().__init__(**kwargs)
    

AB(a='a', b='b')
  • Related