Search code examples
pythonooptraitsmixins

Python mixins : how to deal with *args, **kwargs when calling super()?


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', ''))
               

Solution

  • 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)