Mixin way?

Steven D'Aprano steve+comp.lang.python at pearwood.info
Wed Apr 3 11:47:20 EDT 2013


On Wed, 03 Apr 2013 15:04:51 +0100, andrea crotti wrote:

> I have some classes that have shared behaviours, for example in our
> scenario an object can be "visited", where something that is visitable
> would have some behaviour like

[snip mixins]

By the way, it's a common convention to name mixin classes as "SpamMixin".


> So now I'm not sure how to use it though.
> One way would be multiple subclasses
> 
> class MyObjectBase(object):
>    pass
>
> class MyObj(MyObjectBase, Visitable):
>    pass
> 
> for example.

What's the purpose of MyObjectBase? Either your example is too simple, or 
it actually has no purpose, and you should simply write:

class MyObj(object, VisitableMixin):
    # code goes here


instead of having one extra layer in the inheritance hierarchy.


> This solution is probably easy, but at the same time disturbing because
> MyObjectBase is semantically quite different from Visitable, so
> subclassing from both seems wrong..


Not really. From what you describe, this seems to be exactly the use-case 
for mixins. Yes, mixins are by definition somewhat of an abuse of Object-
Oriented concepts. Mixins don't so much represent a "type of thing" as a 
convenient bundle of encapsulated behaviour and/or state.

E.g. MyObj represents some sort of "MyObj thing" (or at least it 
*should*, if your OO classes are well-planned), but Visitable(Mixin) does 
not represent a thing at all.

But here's a way to look at it to justify the concept of mixins. Think of 
regular classes and subclasses as representing trees of descent, like 
biological groups:

Animal -> Mammal -> Rodent -> Porcupine
Animal -> Monotreme -> Echidna

Then mixins represent convergent evolution of some trait:

Animal -> Mammal -> Rodent + QuillsMixin -> Porcupine
Animal -> Monotreme + QuillsMixin -> Echidna



> The other solution (which is what is partially done now) is to use
> another class attribute:
> 
> class ObjectWithMixin(CouchObject):
>     MIXINS = [Visitable]
> 
> and then do all the smart things needed:
> - at object construction time
> - when setting attributes and so on..
> 
> This solution is more complicated to implement but maybe is more
> flexible and more "correct", what do you think?

I think that's worse.

You seem to have *almost* stumbled onto the design pattern known as 
"composition" or "delegation", only not quite.

Normal class-based design models a "is-a" relationship. "Lassie is a 
Dog", so we would do:

lassie = Dog()

Composition models a "has-a" relationship. For example, both cars and 
boats have engines, so we might be tempted to use a mixin:

class Car(EngineMixin):
    pass

class Boat(EngineMixin):
    pass


which isn't an awful solution. But a conceptually cleaner solution might 
be to do this:

class Car: 
    def __init__(self):
        self.engine = Engine()
    def go(self):
        print "Fasten your seat belt"
        self.engine.start()
        self.handbrake = False
        self.change_gear("drive")
        self.accelerate()

class Boat:
    def __init__(self):
        self.engine = Engine()
    def go(self): ...

and then both cars and boats can *delegate* behaviour to the engine 
object.


So, if you think of "Visitable" as a gadget that can be strapped onto 
your MyObj as a component, then composition is probably a better design. 
But if you think of "Visitable" as a mere collection of behaviour and 
state, then a mixin is probably a better design.



-- 
Steven



More information about the Python-list mailing list