在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):
         ...