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?
Why don't you use mixins directly instead of hacking around in the mro?
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))
class StoryHTML(Story, StoryHTMLMixin):
pass
print StoryHTML('asd','asdf').render() # works just fine
If you really, really, really want to glue extra methods on a class, that is not a big problem. Well, except that it's evil and bad practice. Anyways, you can change a class at any time:
# first, the story
x = Story('asd','asdf')
# changes a class object
def stick_methods_to_class( cls, *methods):
for m in methods:
setattr(cls, m.__name__, m)
# a bare method to glue on
def render(self):
return ("<html><title>%s</title>"
"<body>%s</body></html>"
% (self.name, self.content))
# change the class object
stick_methods_to_class(Story, render)
print x.render()
But in the end, the question remains: Why should classes suddenly grow extra methods? That's the stuff horror movies are made of ;-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With