Logging actions with Python decorators Part III: Mixins
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
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.
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
But seriously, just stick with pure mixins instead of multiple inheritance unless you really know what you’re doing. And even then, be careful!