1

I have lots of defaults being loaded from a config file and stored in a struct-style class (no methods, just variables).

I then have two classes, one defining a Molecule, another defining a specific kind of molecule, here called Ligand which inherits from Molecule. I want Ligand to have access to all methods and variables from Molecule and all variables from DefaultsMixin. I'm trying to use a mixin for this but I think I'm misusing super(). A rough outline of the classes are as follows:

class DefaultsMixin:
    def __init__(self):
        self.memory = 4
        self.threads = 2

class Molecule:
    def __init__(self, name):
        super().__init__(name)
        self.name = name

class Ligand(DefaultsMixin, Molecule):
    def __init__(self, name):
        super().__init__(name)
        self.atoms = ['C', 'H']

Inheritance is right to left, hence the order in Ligand().

I want to avoid using composition as I want to simply call the defaults by name e.g.

# What I want to achieve using mixin
mol = Ligand('methane')
mol.threads
>>> 2

# What I want to avoid using composition
# (self.defaults = Defaults() inside Ligand class instead of using mixin)
mol = Ligand('methane')
mol.defaults.threads
>>> 2

How can I correctly use super() to get this mixin to work?

Thanks for any help.

QuantumChris
  • 963
  • 10
  • 21

2 Answers2

2

You can call the parent object's init method

Please see Calling parent class __init__ with multiple inheritance, what's the right way?

gCoh
  • 2,719
  • 1
  • 22
  • 46
1

If you check the mixin example gCoh's link, you'll see that you need to add *args and **kwargs to the mixin to pass thru unused parameters. Also (seems counter-intuitive to me at least) you need to call super from the mixin not your Molecule class.

Is this the behavior you desire?

class DefaultsMixin:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.memory = 4
        self.threads = 2


class Molecule:
    def __init__(self, name):
        self.name = name


class Ligand(DefaultsMixin, Molecule):
    def __init__(self, name):
        super().__init__(name)
        self.atoms = ['C', 'H']


mol = Ligand('methane')
print(f'The number of threads is {mol.threads}')
print(f'The atoms are {mol.atoms}')
print(f'The name is {mol.name}')

outputs:

The number of threads is 2
The atoms are ['C', 'H']
The name is methane
DaveStSomeWhere
  • 2,475
  • 2
  • 22
  • 19
  • This works (thank you!) but I'm a little confused as to why `DefaultsMixin` needs to take `*args` and `**kwargs` if I'm not passing any variables. – QuantumChris Aug 12 '19 at 13:14
  • 1
    But you are passing a parameter - the name parameter. The mixin init is called first and the name parameter is passed through via the *args and **kwargs in the mixin `super()` that executes the Molecule init. – DaveStSomeWhere Aug 12 '19 at 13:27
  • Ok I think I understand. The mixin takes the argument and passes it across, even if it doesn't use it itself. Great, thank you! – QuantumChris Aug 12 '19 at 13:33
  • 1
    Yes - Legand `super()` calls the Mixin `init()` and the Mixin `super()` calls the Molecule `init()` with the unused args. – DaveStSomeWhere Aug 12 '19 at 13:36