8

I want to provide some functionality to classes using mixin. The functionality uses some additional per-object state. I was wondering what is the cleanest way to initialize this local state. Consider the example:

class Mixin:
    items = []
    def append(self, x):
        self.items.append(x)
    def display(self):
        print self.items

class Foo(object, Mixin): pass
class Bar(object, Mixin): pass

foo = Foo()
foo.append('foo')
foo.display()

>>> ['foo']

bar = Bar()
bar.append('bar')
bar.display()

>>> ['foo', 'bar']

Here, the state is items list. Initializing it in the Mixin body is obviously wrong. Normally, I would initialize it in __init__, but with Mixin I do not want to mess with __init__.

I could do the following:

class Mixin:
    items = None

def append(self, x):
    if self.items is None:
        self.items = []
    self.items.append(x)

But the condition is evaluated on each append and it does not seem to be the cleanest solution.

Any alternatives? Or maybe adding __init__ to the mixin is The way?

(It is a separate question if using mixins is OK or not)

Related:

Community
  • 1
  • 1
Jakub M.
  • 32,471
  • 48
  • 110
  • 179

2 Answers2

4

I would propose to put that in an __init__() of the Mixin. What do you think is the disadvantage?

class Mixin(object):
    def __init__(self, *args, **kwargs):
        super(Mixin, self).__init__(*args, **kwargs)
        self.items = []

I think this is right way to do it; all other (maybe working) solutions look like a hack to me.

Alfe
  • 56,346
  • 20
  • 107
  • 159
  • Should Mixin be inherited from object? – Jakub M. May 24 '13 at 09:16
  • 1
    I assume you mean whether `Mixin` should inherit `object`; I'm going to answer this. Without the inheriting of `object`, the class will be an old-style class. In order to use `super()` correctly, it should be a new-style class, so inherit `object`, yes. – Alfe May 24 '13 at 09:18
  • 2
    You can omit the object in Python 3, but include it anyway, so your code will work on Python 2 as well. – Blubber May 24 '13 at 09:31
  • 1
    Does the type of the mixin actually matter? It will never be instantiated itself and when mixed into a new-style class the class remains new-style. – ThiefMaster May 24 '13 at 09:34
0

The cleanest solution is adding an __init__ method and using super() in each of those methods to ensure every single one is called.

class Mixin:
    def __init__(self, *args, **kwargs):
        self.items = []
        super(Mixin, self).__init__(*args, **kwargs)
    def append(self, x):
        self.items.append(x)
    def display(self):
        print self.items
ThiefMaster
  • 310,957
  • 84
  • 592
  • 636
  • 1
    `super` needs the class to inherit `object` to work properly. – Alfe May 24 '13 at 09:21
  • Ah, thanks Blubber. Good to know. On the other hand, this post isn't about Python3 in particular. – Alfe May 24 '13 at 09:24
  • 1
    True, and in Python 3 you can add object, but it doesn't actually do anything, so it's a good idea to add it anyway. – Blubber May 24 '13 at 09:36
  • Yeah, that's the old problem with the changing constructor signature. *sigh* That has no good solution I know of. One way I typically use is to let each constructor garble up all the parts of the input which it understands (it "consumes" them) and pass on only the ones it doesn't know of. That way the `object`'s `__init__()` will receive an empty `kwargs` which it won't complain about. – Alfe May 24 '13 at 09:36
  • Have a look at http://pastebin.com/MieUwp3p to see what I mean (in case it wasn't clear already). – Alfe May 24 '13 at 09:43