Mixin way?

Discussion in 'Python' started by andrea crotti, Apr 3, 2013.

  1. 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

    --8<---------------cut here---------------start------------->8---
    class Visitable(Mixin):
    FIELDS = {
    'visits': [],
    'unique_visits': 0,
    }

    def record_view(self, who, when):
    self.visits += {'who': who, 'when': when}
    self.unique_visits += 1
    --8<---------------cut here---------------end--------------->8---

    Where the Mixin class simply initialises the attributes:

    --8<---------------cut here---------------start------------->8---
    class Mixin(object):
    def __init__(self, **kwargs):
    for key, val in self.FIELDS.items():
    setattr(self, key, val)

    for key, val in kwargs.items():
    if key in self.FIELDS:
    setattr(self, key, val)
    --8<---------------cut here---------------end--------------->8---


    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.
    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..

    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?
    andrea crotti, Apr 3, 2013
    #1
    1. Advertising

  2. 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
    Steven D'Aprano, Apr 3, 2013
    #2
    1. Advertising

  3. 2013/4/3 Steven D'Aprano <>

    > [snip]
    >
    > 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.
    >
    >

    Well I can explain better the situation to make it more clear.

    We are using CouchDb and so far it has been (sigh) a brutal manipulation of
    dictionaries everywhere, with code duplication and so on.

    Now I wanted to encapsulate all the entities in the DB in proper objects,
    so I have a CouchObject:


    class CouchObject(object) :
    """
    Encapsulate an object which has the ability to be saved to a couch
    database.
    """
    #: list of fields that get filled in automatically if not passed in
    AUTO = ['created_datetime', 'doc_type', '_id', '_rev']
    #: dictionary with some extra fields with default values if not
    # passed in the constructor the default value gets set to the attribute
    DEFAULTS = {}
    REQUIRED = []
    OPTIONAL = []
    TO_RESOLVE = []
    MIXINS = []

    Where every subclass can redefine these attributes to get something
    done automatically by the constructor for convenience.

    Now however there is a lot of behaviour shared between them, so I want
    to encapsulate it out in different places.

    I think the MIXINS as I would use it is the normal composition
    pattern, the only difference is that the composition is done per class
    and not per object (again for lazyness reasons), right?

    Probably subclassing might be fine as well, and makes it simpler, but
    I don't like too much to do subclass from multiple classes...
    andrea crotti, Apr 3, 2013
    #3
  4. andrea crotti

    Neil Cerutti Guest

    On 2013-04-03, andrea crotti <> wrote:
    > Well I can explain better the situation to make it more clear.
    >
    > We are using CouchDb and so far it has been (sigh) a brutal
    > manipulation of dictionaries everywhere, with code duplication
    > and so on.
    >
    > Now I wanted to encapsulate all the entities in the DB in
    > proper objects, so I have a CouchObject:
    >
    >
    > class CouchObject(object) :
    > """
    > Encapsulate an object which has the ability to be saved to a couch
    > database.
    > """
    > #: list of fields that get filled in automatically if not passed in
    > AUTO = ['created_datetime', 'doc_type', '_id', '_rev']
    > #: dictionary with some extra fields with default values if not
    > # passed in the constructor the default value gets set to the attribute
    > DEFAULTS = {}
    > REQUIRED = []
    > OPTIONAL = []
    > TO_RESOLVE = []
    > MIXINS = []
    >
    > Where every subclass can redefine these attributes to get
    > something done automatically by the constructor for
    > convenience.


    Hopefully someone with experience with them can help you further,
    but this seems like a job for a metaclass.

    --
    Neil Cerutti
    Neil Cerutti, Apr 3, 2013
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Udo Gleich

    mixin class

    Udo Gleich, Jul 28, 2003, in forum: Python
    Replies:
    5
    Views:
    435
    Jeremy Bowers
    Jul 31, 2003
  2. Mac
    Replies:
    1
    Views:
    570
  3. Paolino
    Replies:
    0
    Views:
    269
    Paolino
    Aug 16, 2005
  4. Replies:
    8
    Views:
    701
    George Sakkis
    Oct 7, 2005
  5. Replies:
    0
    Views:
    337
Loading...

Share This Page