1

I am trying to create a mixin class that has it's own properties, but as the class has no init to initialize the "hidden" variable behind the property.

class Software:

    __metaclass__ = ABCMeta

    @property
    def volumes(self):
        return self._volumes

    @volumes.setter
    def volumes(self, value):
        pass

class Base(object):

    def __init__(self):
        self._volumes = None

class SoftwareUser(Base, Software):

    def __init__(self):
        super(Base, self).__init__()

So above is the best that I have come up with to solve this but the reality is that the _volumes dosn't really belong in the base. I could add an init to the Software class but then the super call wont work on both mixins.

The second is that I will need multiple mixins dependent on the incoming call they will always need the base, but the mixins will change so I dont really want variables from mixins that aren't mixed in for that call.

Is there a way that i can have the mixin add it's variables to the class if it is mixed in perhaps dynamically call the init of the mixin class ?.

Any questions let me know.

Thanks

John Dowling
  • 582
  • 6
  • 23
  • It's not clear to me what you mean by *"I could add an init to the Software class but then the super call wont work on both mixins"*. All three classes should correctly implement `__init__` and initialise any attributes they need. – jonrsharpe Sep 13 '16 at 12:29
  • So what I mean by that is in the example above add an init with the _volumes variable to the software class, but If I do this and it is mixed in with the base class as it is in the example(SoftwareUser), then the init of the Software class will not be called only the Base init will be called – John Dowling Sep 13 '16 at 12:34
  • Not if you implement them all correctly, to ensure that any other implementations in the MRO are called. Only `Base` should not call `super`, and it should be the *last* inherited class. – jonrsharpe Sep 13 '16 at 12:36
  • But they are not running in a chain of inheritance, so this I dont think works, unless I am missing something. I am currently looking at this http://stackoverflow.com/a/6100595/1638158 perhaps this is overkill, if there is a way to simplfy then i am open – John Dowling Sep 13 '16 at 12:51
  • You are mixing a class into an inheritance chain. It needs to deal correctly with that, by calling the next implementation of any of its methods in the MRO, *whatever class it's mixed into*. Mix in classes should appear *before* the base class in the definitions of subclasses using them. – jonrsharpe Sep 13 '16 at 13:25

2 Answers2

3

Yes, that's wildly overcomplicated. A class (including mixins) should only be responsible for calling the next implementation in the MRO, not marshalling all of them. Try:

class Software:

    @property
    def volumes(self):
       return self._volumes

    @volumes.setter
    def volumes(self, value):
       pass

    def __init__(self):
        self._volumes = None
        super().__init__()  # mixin calls super too


class Base(object):

    def __init__(self):
        other_vars = None


class SoftwareUser(Software, Base):  # note order

    def __init__(self):
        super().__init__()  # all you need here
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • Ah, thanks that was the missing piece, i get it. it is not that intuitive that super will follow the MRO, I was expecting to have to inherit base from the "Software" class to be able to use the super call – John Dowling Sep 13 '16 at 14:23
  • To note: `super().method()` only works in Python 3. In 2, you need to do `super(ThisClass, self).method()` – justanr Sep 14 '16 at 22:39
  • Am I right in thinking that `Software.__init__` should have signature `(self, *args)`, and that it should pass `*args` to its `super().__init__`? Otherwise if `Base.__init__` is ever modified to take arguments, it's impossible for `SoftwareUser.__init__` to pass them? – Cai Mar 08 '21 at 10:14
  • 1
    @Cai yes, in general when implementing (especially multiple) inheritance you should be handling `*args` and `**kwargs`. – jonrsharpe Mar 08 '21 at 10:15
1

Ok so here is what I came up with, I am open to other answers, if I have made this way over complicated.

class Software:

    @property
    def volumes(self):
       return self._volumes

    @volumes.setter
    def volumes(self, value):
       pass

    def __init__(self):
        self._volumes = None


class Base(object):

    def __init__(self):
        other_vars = None


class SoftwareUser(Base, Software):

    def _bases_init(self, *args, **kwargs):
        for base in type(self).__bases__:
            base.__init__(self, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        self._bases_init(*args, **kwargs)
John Dowling
  • 582
  • 6
  • 23