Esc*_*alo 6 python multiple-inheritance mixins diamond-problem
请考虑以下代码实现一个简单的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()
Run Code Online (Sandbox Code Playgroud)
运行main会导致以下错误:
TypeError: Cannot create a consistent method resolution
order (MRO) for bases object, StoryHTMLMixin
Run Code Online (Sandbox Code Playgroud)
问题是,无论Story和StoryHTMLMixin衍生自object和钻石的问题就出现了.
解决方案只是创建StoryHTMLMixin一个旧式类,即删除继承object,从而将类的定义更改StoryHTMLMixin为:
class StoryHTMLMixin:
def render(self):
return ("<html><title>%s</title>"
"<body>%s</body></html>"
% (self.name, self.content))
Run Code Online (Sandbox Code Playgroud)
运行时会导致以下结果main:
<html><title>My Life</title><body><p>Is good.</p></body></html>
Run Code Online (Sandbox Code Playgroud)
我不喜欢使用旧式课程,所以我的问题是:
这是在Python中处理这个问题的正确方法,还是有更好的方法?
编辑:
我看到类UserDict在最新的Python源定义一个mixin诉诸老样式类(如在我的例子给出).
根据所有人的建议,我可以在不使用MixIns的情况下重新定义我想要实现的功能(即,在运行时绑定方法).然而,这一点仍然存在 - 这是唯一一个在没有重新实现或退回旧式课程的情况下弄乱MRO无法解决的用例吗?
你为什么不直接使用mixins而不是在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
Run Code Online (Sandbox Code Playgroud)
如果你真的,真的,真的想在类上粘贴额外的方法,那不是一个大问题.好吧,除了它是邪恶的和坏的做法.无论如何,您可以随时更改课程:
# 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()
Run Code Online (Sandbox Code Playgroud)
但最终,问题仍然存在:为什么课程会突然增加额外的方法?这就是恐怖片制作的东西;-)
__bases__如果我们消除魔法并明确编写您正在创建的类,则可以更轻松地看到发生了什么:
class StoryHTMLMixin(object):
def render(self):
return ("<html><title>%s</title>"
"<body>%s</body></html>"
% (self.name, self.content))
class Story(object, StoryHTMLMixin):
def __init__(self, name, content):
self.name = name
self.content = content
Run Code Online (Sandbox Code Playgroud)
这就是你正在做的事情的最终结果——或者如果成功的话将会是什么。它会导致相同的错误。
请注意,这实际上并不是钻石继承。这涉及四个类,其中两个基类各自继承一个共同的第四个类;Python 的多重继承解决了这个问题。
这里只有三个类,导致继承如下所示:
StoryHTMLMixin <--- Story
| _____/
| |
v v
object
Run Code Online (Sandbox Code Playgroud)
Python 不知道如何解决这个问题。
我不知道这个问题的解决方法。原则上,解决方案是在添加的同时object从基础中删除,但由于有些不透明的内部原因,这是不允许的()。StoryStoryHTMLMixinTypeError: __bases__ assignment: 'StoryHTMLMixin' deallocator differs from 'object'
无论如何,我从未发现任何实际的、现实世界的用途来修改这样的类。它看起来很混乱——如果您想要一个从这两个类派生的类,只需正常创建一个类即可。
埃德:
这是一种与您的方法类似的方法,但无需就地修改类。请注意它如何返回一个新类,从函数的参数动态派生。这更加清楚——例如,您不能无意中修改已经实例化的对象。
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, name=None):
if name is None:
name = "mixed_%s_with_%s" % (TargetClass.__name__, MixInClass.__name__)
class CombinedClass(TargetClass, MixInClass):
pass
CombinedClass.__name__ = name
return CombinedClass
if __name__ == "__main__":
MixedStory = MixIn(Story, StoryHTMLMixin, "MixedStory")
my_story = MixedStory("My Life", "<p>Is good.</p>")
print my_story.render()
Run Code Online (Sandbox Code Playgroud)