Composition instead of inheritance

Carl Banks pavlovevidence at
Fri Apr 29 17:09:01 EDT 2011

On Thursday, April 28, 2011 6:43:35 PM UTC-7, Ethan Furman wrote:
> Carl Banks wrote:
> > The sorts of class that this decorator will work for are probably not
>  > the ones that are going to have problems cooperating in the first place.
>  > So you might as well just use inheritance; that way people trying to read
>  > the code will have a common, well-known Python construct rather than a
>  > custom decorator to understand.
>  From thread 'python and super' on Python-Dev:
> Ricardo Kirkner wrote:
>  > I'll give you the example I came upon:
>  >
>  > I have a TestCase class, which inherits from both Django's TestCase
>  > and from some custom TestCases that act as mixin classes. So I have
>  > something like
>  >
>  > class MyTestCase(TestCase, Mixin1, Mixin2):
>  >    ...
>  >
>  > now django's TestCase class inherits from unittest2.TestCase, which we
>  > found was not calling super.
> This is the type of situation the decorator was written for (although 
> it's too simplistic to handle that exact case, as Ricardo goes on to say 
> he has a setUp in each mixin that needs to be called -- it works fine 
> though if you are not adding duplicate names).

The problem is that he was doing mixins wrong.  Way wrong.

Here is my advice on mixins:

Mixins should almost always be listed first in the bases.  (The only exception is to work around a technicality.  Otherwise mixins go first.)

If a mixin defines __init__, it should always accept self, *args and **kwargs (and no other arguments), and pass those on to super().__init__.  Same deal with any other function that different sister classes might define in varied ways (such as __call__).

A mixin should not accept arguments in __init__.  Instead, it should burden the derived class to accept arguments on its behalf, and set attributes before calling super().__init__, which the mixin can access.

If you insist on a mixin that accepts arguments in __init__, then it should should pop them off kwargs.  Avoid using positional arguments, and never use named arguments.  Always go through args and kwargs.

If mixins follow these rules, they'll be reasonably safe to use on a variety of classes.  (Maybe even safe enough to use in Django classes.)

Carl Banks

More information about the Python-list mailing list