2

I'd like to submit a code pattern I often see in my python code, here at work, and I'm not satisfy with it and I'd like a better solution.

Here is what we have today:

class AbstractClass(object):
    ....
    def method1(self,...)
       raise NotImplementedError
    ...

class FirstImplementationMixin(object)
    def method1(self,....) 
       ...

class SecondImplementationMixin(object)
    def method1(self...)
       ....

class InstanciatedClass1(FirstImplementationMixin, AbstractClass)
    ....

class InstanciatedClass2(SecondImplementationMixin, AbstractClass)
    ....

Do you see the trick? I have to add the mixin at first position in the list of the inheritance, and I don't like this solution. If we add it at the second position, the interpreter will use AbstractClass.method1 and so raise the exception.

In this trivial situation, replacing the mixin by intermediate class is possible, but in case of complex inheritance with already a base class, the solution might not be obvious.

What is for you the best design pattern?

Gaetan
  • 488
  • 4
  • 13
  • 2
    Well, that's how Python works. When you don't have traits/mixins as a separate concept in your language you simply mix them with classes. And listing classes in order of their “priority” is a very natural thing to do. – kirelagin Jun 05 '13 at 17:35

1 Answers1

1

Well, as I noted in my comment this is just how Python works, and I don't think that changing it is a good idea.

Nevertheless, metaclasses to the rescue!
You can create your own metaclass and make it as complex as you need. Here I'll show you a very basic example.

def mixed(name, bases, dct):
    return type(name, bases[1:] + (bases[0],), dct)

It simply moves the first baseclasses to the end, so now you put your actual base-class first, followed by all the mixins. That means that you are limited to a single base class. You can lift this limitation by some convention, for example, you'll list base classes first, then None, then mixins; and your metaclass will look for this None and reorder the tuple accordingly.

class MixinA:
    pass

class MixinB:
    pass

class Base(object):
    pass

class Test(Base, MixinA, MixinB):
    __metaclass__ = mixed
>>> Test.__mro__
                 >
(<class '__main__.Test'>,
 <class __main__.MixinA at 0x6f8850db48>,
 <class __main__.MixinB at 0x6f8850dae0>,
 <class '__main__.Base'>,
 <type 'object'>)

Of course you won't be specifying __metaclass__ each time, because of the rules that are used to chose a metaclass. You'll probably set __metaclass__ on the module level or, maybe, in your mixins. That's your choice, whatever fits your code structure better.

Community
  • 1
  • 1
kirelagin
  • 13,248
  • 2
  • 42
  • 57