Please consider the following code implementing a simple MixIn:
class Story(object):
def __init__(self, name, content):
self.name = name
self.content = content
class StoryHTMLMixin(object):
def render(self):
return ("<html><title>%s</title>"
"<body>%s</body></html>"
% (self.name, self.content))
def MixIn(TargetClass, MixInClass):
if MixInClass not in TargetClass.__bases__:
TargetClass.__bases__ += (MixInClass,)
if __name__ == "__main__":
my_story = Story("My Life", "<p>Is good.</p>")
# plug-in the MixIn here
MixIn(Story, StoryHTMLMixin)
# now I can render the story as HTML
print my_story.render()
Running main leads to the following error:
TypeError: Cannot create a consistent method resolution
order (MRO) for bases object, StoryHTMLMixin
The problem is that both Story and StoryHTMLMixin are derived from object, and the diamond problem arises.
The solution is simply to make StoryHTMLMixin an old-style class, i.e., remove the inheritance from object, thus, changing the definition of the class StoryHTMLMixin to:
class StoryHTMLMixin:
def render(self):
return ("<html><title>%s</title>"
"<body>%s</body></html>"
% (self.name, self.content))
leads to the following result when running main:
<html><title>My Life</title><body><p>Is good.</p></body></html>
I don’t like having to use old style classes, so my question is:
Is this the correct way to handle this problem in Python, or is there a better way?
Edit:
I see that the class UserDict in the latest Python source defines a MixIn resorting to the old style classes (as presented in my example).
As recommended by all, I may resort to redefining the functionality that I want to attain (namely, the binding of methods at run time) without using MixIns. However, the point still remains – is this the only use case where messing with the MRO is unsolvable without resorting to reimplementation or falling back to old-style classes?
It’s easier to see what’s happening if we eliminate the
__bases__magic and write our the classes you’re creating explicitly:That’s the end result of what you’re doing–or what it would be if it succeeded. It results in the same error.
Notice that this isn’t actually diamond inheritance. That involves four classes, where two base classes each inherit a common fourth class; Python’s multiple inheritance deals with that.
Here you have just three classes, leading to inheritance that looks like this:
Python doesn’t know how to resolve this.
I don’t know about a workaround for this. In principle the solution would be to remove
objectfrom the bases ofStoryat the same time you addStoryHTMLMixinto it, but that isn’t allowed for somewhat opaque, internal reasons (TypeError: __bases__ assignment: 'StoryHTMLMixin' deallocator differs from 'object').I’ve never found any practical, real-world use for modifying classes like this anyway. It just seems obfuscated and confusing–if you want a class that derives from those two classes, just create a class normally.
Ed:
Here’s an approach that does something similar to yours, but without modifying classes in-place. Note how it returns a new class, deriving dynamically from the arguments of the function. This is much clearer–you can’t inadvertently modify objects that are already instantiated, for example.