views:

41

answers:

1

I've written a mixin class that's designed to be layered on top of a new-style class, for example via

class MixedClass(MixinClass, BaseClass):
    pass

What's the smoothest way to apply this mixin to an old-style class? It is using a call to super in its __init__ method, so this will presumably (?) have to change, but otherwise I'd like to make as few changes as possible to MixinClass. I should be able to derive a subclass that makes the necessary changes.

I'm considering using a class decorator on top of a class derived from BaseClass, e.g.

@old_style_mix(MixinOldSchoolRemix)
class MixedWithOldStyleClass(OldStyleClass)

where MixinOldSchoolRemix is derived from MixinClass and just re-implements methods that use super to instead use a class variable that contains the class it is mixed with, in this case OldStyleClass. This class variable would be set by old_style_mix as part of the mixing process.

old_style_mix would just update the class dictionary of e.g. MixedWithOldStyleClass with the contents of the mixin class (e.g. MixinOldSchoolRemix) dictionary.

Is this a reasonable strategy? Is there a better way? It seems like this would be a common problem, given that there are numerous available modules still using old-style classes.

+2  A: 

This class variable would be set by old_style_mix as part of the mixing process.

...I assume you mean: "...on the class it's decorating..." as opposed to "on the class that is its argument" (the latter would be a disaster).

old_style_mix would just update the class dictionary of e.g. MixedWithOldStyleClass with the contents of the mixin class (e.g. MixinOldSchoolRemix) dictionary.

No good -- the information that MixinOldSchoolRemix derives from MixinClass, for example, is not in the former's dictionary. So, old_style_mix must take a different strategy: for example, build a new class (which I believe has to be a new-style one, because old-style ones do not accept new-style ones as __bases__) with the appropriate sequence of bases, as well as a suitably tweaked dictionary.

Is this a reasonable strategy?

With the above provisos.

It seems like this would be a common problem, given that there are numerous available modules still using old-style classes.

...but mixins with classes that were never designed to take mixins are definitely not a common design pattern, so the problem isn't common at all (I don't remember seeing it even once in the many years since new-style classes were born, and I was actively consulting, teaching advanced classes, and helping people with Python problems for many of those years, as well as doing a lot of software development myself -- I do tend to have encountered any "reasonably common" problem that people may have with features which have been around long enough!-).

Here's example code for what your class decorator could do (if you prefer to have it in a class decorator rather than directly inline...):

>>> class Mixo(object):
...   def foo(self):
...     print 'Mixo.foo'
...     self.thesuper.foo(self)
... 
>>> class Old:
...   def foo(self):
...     print 'Old.foo'
... 
>>> class Mixed(Mixo, Old):
...   thesuper = Old
... 
>>> m = Mixed()
>>> m.foo()
Mixo.foo
Old.foo

If you want to build Mixed under the assumed name/binding of Mixo in your decorator, you could do it with a call to type, or by setting Mixed.__name__ = cls.__name__ (where cls is the class you're decorating). I think the latter approach is simpler (warning, untested code -- the above interactive shell session is a real one, but I have not tested the following code):

def oldstylemix(mixin):
    def makemix(cls):
        class Mixed(mixin, cls):
            thesuper = cls
        Mixed.__name__ = cls.__name__
        return Mixed
    return makemix
Alex Martelli