如何动态地添加混入作为基类没有得到MRO错误?(How do I dynamically add m

2019-07-28 23:11发布

说我有一类ABC

AB都混入类类C

class A( object ):
    pass
class B( object ):
    pass
class C( object, A, B ):
    pass

这将无法正常工作实例类C.当我将不得不删除object从C类,使其工作。 (否则你会得到MRO问题)。

类型错误:调用元类基地时出错
无法创建一个一致的方法解析
顺序(MRO)为碱B,对象,A

然而,我的情况有点复杂。 在我的案例类C是其中一台服务器 AB将是在启动时加载的插件。 这些居住在自己的文件夹。

我也有一个名为类Cfactory 。 在Cfactory我有一个__new__ ,将创建一个全功能的对象C.在该方法__new__方法我搜索插件,使用加载它们__import__ ,然后将其分配给C.__bases__ += (loadedClassTypeGoesHere, )

所以,下面是一个可能性:(说得很抽象)

class A( object ):
    def __init__( self ): pass
    def printA( self ):   print "A"
class B( object ):
    def __init__( self ): pass
    def printB( self ):   print "B"
class C( object ):
    def __init__( self ):  pass
class Cfactory( object ):
    def __new__( cls ):
        C.__bases__ += ( A, )
        C.__bases__ += ( B, )
        return C()

这再次将无法正常工作,并会再次给MRO错误:

类型错误:无法创建一个一致的方法解析
顺序(MRO)为基础对象,

一个简单的办法解决这个被除去object从基类AB 。 然而,这将使得当这些插件被独立运行,应避免他们旧式的对象(这应该是可能的,单元测试明智)

另一个简单的办法是消除objectC但是这也将使它成为一个旧式类和C.__bases__将不可用,因此我不能额外的对象添加到基础C

什么将是一个很好的架构解决方案,你会怎么做这样的事? 现在我可以用旧式类插件本身生活。 但我宁可不使用它们。

Answer 1:

想想这样说-你想要的混入,以覆盖某些行为的object ,所以他们需要之前object的方法解析顺序。

所以,你需要改变基地的顺序:

class C(A, B, object):
    pass

由于这个错误 ,你需要C不从对象继承直接能够正确地分配给__bases__ ,工厂确实可能只是一个功能:

class FakeBase(object):
    pass

class C(FakeBase):
    pass

def c_factory():
    for base in (A, B):
        if base not in C.__bases__:
            C.__bases__ = (base,) + C.__bases__
    return C()


Answer 2:

我不知道的细节,所以我可能完全关闭基地在这里,但好像你正在使用错误的机制来实现你的设计。

首先,为什么是Cfactory一个类,为什么它的__new__方法返回的事物的实例? 这看起来像实现的东西是很自然的一个功能,一个奇怪的方式。 Cfactory如你所描述它(和示出一个简化的例子)不表现在所有像的类; 你不要有多个实例的认为共享功能(其实它看起来像你做不可能建立的自然实例)。

说实话, C不会看起来非常像一个类来我满意。 好像你不能创建它的多个实例,否则你最终与日益增长的基地名单。 这样就使得C基本上是一个模块,而不是一类,只有额外的样板。 我会尽量避免“单实例类来表示应用程序或某些外部系统”模式(虽然我知道这是流行因为Java,您需要使用它)。 但该类继承机制往​​往可以很方便的事情是不是真的类,比如你的插件系统。

我会一直这样做有一个类方法C找到并加载插件,通过该模块定义调用C所以它始终处于良好状态。 另外,您可以使用元类自动添加它找到的类基地任何插件。 混合机制与机制创建类的实例构成类别似乎是错误的; 它是柔性的去耦设计相反。

如果插件无法在当时被加载C被创建,然后我会去的点处,手动调用配置器类方法时,你可以搜索插件,在之前C被创建的实例。

实际上,如果类不能尽快它的创建放入一个一致的状态,我可能宁愿去动态地创建类不是修改现有类的基础。 然后系统没有被锁定到类被配置一次,并且实例化一次; 你对具有不同组加载的插件的多个实例的可能性至少开放。 事情是这样的:

def Cfactory(*args, **kwargs):
    plugins = find_plugins()
    bases = (C,) + plugins
    cls = type('C_with_plugins', bases, {})
    return cls(*args, **kwargs)

这样一来,你有你的单次调用来创建你C实例与给你一个正确配置的实例,但是它不会对其他任何假设的情况下,奇怪的副作用C可能已经存在,且其行为不依赖于不管是被之前运行。 我知道你可能并不需要或者这两个属性的,但比你在简化的例子有它的几乎没有更多的代码,为什么打破什么类,如果你没有概念模型?



Answer 3:

有一个简单的解决方法:创建一个辅助类,有一个好听的名字,像PluginBase。 并使用了的继承,而不是目的。

这使得代码更易读(恕我直言),并将其情况的bug。

class PluginBase(object): pass
class ServerBase(object): pass

class pluginA(PluginBase): "Now it is clearly a plugin class"
class pluginB(PluginBase): "Another plugin"

class Server1(ServerBase, pluginA, pluginB): "This works"
class Server2(ServerBase): pass
Server2.__base__ += (PluginA,) # This also works

随着注:也许你并不需要的工厂 ; 它需要在C ++中,但几乎没有在Python



文章来源: How do I dynamically add mixins as base classes without getting MRO errors?