Reputation: 7809
I have a simple mixin structure in Python. The code should be pretty self-explaining:
class Base:
def __init__(self):
pass
class MixinA:
def __init__(self):
self.x = 0
self.y = 1
def a(self):
print('A: x = ' + str(self.x) + ', y = ' + str(self.y))
class MixinB:
def __init__(self):
self.x = 2
self.z = 3
def b(self):
print('B: x = ' + str(self.x) + ', z = ' + str(self.z))
class MyFirstMix(MixinA, MixinB, Base):
def __init__(self):
Base.__init__(self)
MixinB.__init__(self)
MixinA.__init__(self)
class MySecondMix(MixinA, Base):
def __init__(self):
Base.__init__(self)
MixinA.__init__(self)
I'd like to improve this a bit, so this leads to 3 questions/problems:
MixinA
and MixinB
both have a member x
. Is there a way to make sure, each of the class sees only its own x?
As far as I know: No, there isn't.mix = Base() with MixinA
If my proposed structure is completely wrong, I'm also open for other recommendations on how to handle mixins.
Upvotes: 2
Views: 1393
Reputation: 2011
For python class inherent, I believe there are some tricks you need to know:
Class in python2 and python3 are quite different.
Python2 support old-style class, but python3 support new-style class only. Simply speaking: in python3, classes always inherent from a base class object
, even though you do not explicitly use it. Check Difference-between-Old-New-Class.
Method Resolution Order (MRO). This determines how derived class search inherent members and functions. See MRO
super
function. Combined with MRO, you can easily call parent member or function, without explicitly know the name of parent class. See Super
Now comes to you questions:
I don't quit understand your meaning. When you refer a class member, you must refer it through its instance or class. So instance_of_MixinA.x
and instance_of_MixinB.x
are separated. If you are talking about class MyFirstMix(MixinA, MixinB, Base)
, it depends on how __init__
function is called. In your code, you first populate x
by MixinB
and then reset its value by MixinA
.
Your designation make it impossible. You have to call all constructors.
I am not sure. But I can give you some suggestions: try outside __init__
members when def class (python3, if you used python2 take care of super
):
class Base:
def __init__(self):
pass
class MixinA:
x = 0
y = 1
class MixinB:
x = 2
z = 3
def b(self):
print('B: x = ' + str(self.x) + ', z = ' + str(self.z))
class MyFirstMix(MixinA, MixinB, Base):
def __init__(self):
super().__init__()
class MySecondMix(MixinA, Base):
def __init__(self):
super().__init__()
The variables outside __init__
behaves quit different from inside variables: outside variables belongs to class and will be populated for all instances of this class, while inside variables belongs only to instance (referred by self
when you define class), and will be populated only if __init__
is called. That's why you cannot use super
to call all the constructors---super
only call the priority parent's __init__
. See variables-outsite-inside-init
This is a good solution to Mixin class. In above code, MyFirstMix
inherents both MixinA
and MixinB
whose members are all class members (outside __init__
). So instances of MyFirstMix
will inherent all class members of MixinA
and MixinB
without call __init__
. Here MixinA
and MixinB
own same class member x
, but the MRO
determines that when instances of MyFirstMix
refer x
, x
from MixinA
should be returned.
Hope this will be helpful. Thanks!
Upvotes: 2
Reputation: 37033
When your inheritance schemes start to suffer from these sorts of issues it's time to consider using a technique called composition instead. A good readable introduction to the topic here. The Wikipedia example is a bit less accessible, but also useful if you can handle the other programming languages. This StackExchange question also offers useful discussion.
At its simplest, rather than a class inheriting from SomeParent
and mixing in the Mixin
class, you instead have the SomeParent
instances each create an instance of Mixin
and use that to access the mixin class's functionality.
Upvotes: 1