在Python中使用mixin时出现菱形问题 请考虑以下代码实现一个简单的 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()
问题是在Python中使用mixin时出现菱形问题 请考虑以下代码实现一个简单的 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(),python,multiple-inheritance,mixins,diamond-problem,Python,Multiple Inheritance,Mixins,Diamond Problem,问题是Story和storythmixin都是从对象派生的,因此出现了 解决方案只是将StoryHTMLMixin作为一个旧式类,即从object中删除继承,从而将类StoryHTMLMixin的定义更改为: class StoryHTMLMixin: def render(self): return ("<html><title>%s</title>" "<body>%s</body></ht
Story
和storythmixin
都是从对象
派生的,因此出现了
解决方案只是将StoryHTMLMixin
作为一个旧式类,即从object
中删除继承,从而将类StoryHTMLMixin
的定义更改为:
class StoryHTMLMixin:
def render(self):
return ("<html><title>%s</title>"
"<body>%s</body></html>"
% (self.name, self.content))
我不喜欢使用老式的类,所以我的问题是:
这是在Python中处理此问题的正确方法,还是有更好的方法?
编辑:
我看到中的类UserDict
定义了一个使用旧式类的MixIn(如我的示例所示)
正如大家所建议的那样,我可以求助于重新定义我想要实现的功能(即,在运行时绑定方法),而不使用mixin。然而,问题仍然存在——这是唯一一个在不重新实现或回到旧式类的情况下无法解决MRO问题的用例吗?您可以尝试使用装饰器来添加功能:
def render(obj):
return ("<html><title>%s</title>"
"<body>%s</body></html>"
% (obj.name, obj.content))
def renderable(cls):
cls.render = render
return cls
@renderable
class Story(object):
...
def渲染(obj):
返回(“%s”)
%s
%(对象名称、对象内容))
def可渲染(cls):
cls.render=render
返回cls
@可渲染
班级故事(对象):
...
编辑:我的错,这个bug是另外一个问题(谢谢@Glenn Maynard)。只要您的mixin直接从对象继承,下面的hack仍然有效
class Text(object): pass
class Story(Text):
....
然而,我认为混合并不是解决你问题的最简单的方法。提供的其他两种解决方案(类装饰器和普通子类)都清楚地将故事
类标记为可渲染,而您的解决方案隐藏了这一事实。也就是说,render
方法在其他解决方案中的存在是明确的,而在您的解决方案中是隐藏的。我认为这将在以后引起混乱,特别是如果您更加依赖mixin方法
就我个人而言,我喜欢类装饰器。你为什么不直接使用mixin而不是在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
类故事(对象):
定义初始化(自我、姓名、内容):
self.name=名称
self.content=内容
类StoryHTMLMixin(对象):
def渲染(自):
返回(“%s”)
%s
%(self.name,self.content))
类StoryHTML(Story,StoryHTMLMixin):
通过
打印故事HTML('asd','asdf')。render()#工作正常
如果你真的,真的,真的想在一个类上粘贴额外的方法,这不是一个大问题。好吧,除了那是邪恶的坏习惯。无论如何,您可以随时更改类:
# 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()
#首先是故事
x=故事('asd','asdf')
#更改类对象
def粘贴方法到类(cls,*方法):
对于m in方法:
setattr(cls,m.__名称_;,m)
#一种简单的粘合方法
def渲染(自):
返回(“%s”)
%s
%(self.name,self.content))
#更改类对象
将方法粘贴到类(故事、渲染)
打印x.render()
但最终,问题仍然是:为什么类突然增加了额外的方法?这就是恐怖电影的素材;-) 如果我们消除\uuuuuu base\uuuuuuu
的魔力,并明确地编写您正在创建的类,那么更容易看到发生了什么:
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
然而,问题仍然存在,那就是
这是唯一一个使用混乱的用例
没有MRO是无法解决的
求助于重新实施或
回到旧式课堂
其他人则提出了更好的解决方案——比如明确构建
所需的类——但要回答您编辑的问题,可以定义
在不使用旧式类的情况下进行混音:
class Base(object): pass
class Story(Base):
def __init__(self, name, content):
self.name = name
self.content = content
class StoryHTMLMixin(Base):
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,)+TargetClass.__bases__
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()
类基(对象):传递
班级故事(基础):
定义初始化(自我、姓名、内容):
self.name=名称
self.content=内容
类StoryHTMLMixin(基):
def渲染(自):
返回(“%s”)
%s
%(self.name,self.content))
def混合(目标类、混合类):
如果MixInClass不在TargetClass中。\uuuu base\uuuuuu:
TargetClass.\uuuuuuuuuuu基=(MixInClass,)+TargetClass.\uuuuuuuu基__
如果名称=“\uuuuu main\uuuuuuuu”:
我的故事(我的生活很好)
#在这里插入MixIn
混合(故事,故事混合)
#现在我可以将故事呈现为HTML
打印我的故事。render()
屈服
<html><title>My Life</title><body><p>Is good.</p></body></html>
我的生活很好
除了前面的答案所说的一切(以及邪恶和坏主意),你的问题是:
基地应该以另一种方式排列
TargetClass.\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=(MixInClass,)+TargetClass.\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
-最简单的解决方法是从用户定义的类而不是对象继承所有内容,如
class MyBase(object): pass
class Story(MyBase):
...
class StoryHTMLMixin(MyBase):
...
在Python中,切换回旧式类从来都不是处理任何事情的正确方法——它们甚至不再存在于Python 3中。Python已经通过对基类使用C3排序解决了菱形问题。您正在手动破坏该订单,因此维护MRO的一致性是您的责任:。我同意有很多方法可以动态地添加方法(正如我在这里介绍的,有些人非常反对mixin)。然而,问题依然存在,你能在不使用老式课程的情况下解决钻石问题吗?谢谢。这不再是一个混合;它只是一个多重继承的子类。虽然我同意这一点,但我认为mixin只是一个没有\uuuu init\uuuu
的类,至少SocketServer
模块就是这样做的。对我来说,OP所做的似乎更像是蒙基补丁。@THC:的确如此!很抱歉,创建另一个类不是MixIn,因为如果在其他地方使用Story对象,它将看不到MixIn方法。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, 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()
class Base(object): pass
class Story(Base):
def __init__(self, name, content):
self.name = name
self.content = content
class StoryHTMLMixin(Base):
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,)+TargetClass.__bases__
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()
<html><title>My Life</title><body><p>Is good.</p></body></html>
class MyBase(object): pass
class Story(MyBase):
...
class StoryHTMLMixin(MyBase):
...