Skip to Main Content
×
Freshbooks
Official App
Free – Google Play
Get it
FreshBooks is Loved by American Small Business Owners
FreshBooks is Loved by Canadian Small Business Owners
FreshBooks is Loved by Small Business Owners in the UK
Dev Blog

Logging actions with Python decorators Part III: Mixins

by Taavi on June 14/2011

Sorry for the year’s hiatus. It’s been busy, and I’m hoping to get a flurry of blog posts out about it shortly. But for now, let’s get down to finishing this miniseries. :)

For part 3 we’ll look at something completely different. Mixins are a way of using multiple inheritance (yes, Python has it!). Most commonly, they’re used to add a set of common helper functions to a class, such as a test class. Consider a test class that inherits from MyFooTestBase for most of its setUp(), but also needs the commonly-used assert_bunnies() assertion:

class TestHelper(object):
    def assert_bunnies(self, a_string):
        assert a_string == "bunnies", "Failed to find bunnies"
class TestMyClass(MyFooTestBase, TestHelper):
    def test_foo(self):
        value = self.foo()
        self.assert_bunnies(value)

When your method names are distinct, you can mix and match as much as you like, but be careful with your __init__() method if you’re using Python’s built-in unittest module; it wants a specific function signature that you probably don’t want to deal with (see below for dealing with multiple inheritance and __init__()). I’d recommend just using nosetests as your test collector to avoid having to subclass unittest.TestCase) at all.

When the method names aren’t distinct, there’s a well-defined method resolution order (MRO) documented. It hasn’t changed in Python 2.7 or even Python 3.1.

Of course, things are trickier than that. Overriding a method like __init__() generally means having to call your superclass…but which class is that? The MRO handles it, but not always as you might expect. Others have written on how you can’t use it (unless you follow some guidelines).

In the past year, our code’s been refactored and a mixin no longer makes sense. We still use the decorators, though, but instead of a self._log_event method, they expect to find an self._event_log_manager object. That change wasn’t because of a problem with using mixins, but because the code has been refactored so that an event log manager is instantiated per service (e.g. a ProjectService) but we log changes to resources (e.g. a TaskResource and ProjectResource).

But seriously, just stick with pure mixins instead of multiple inheritance unless you really know what you’re doing. And even then, be careful! ;)