说我有一类A
, B
和C
。
类A
和B
都混入类类C
。
class A( object ):
pass
class B( object ):
pass
class C( object, A, B ):
pass
这将无法正常工作实例类C.当我将不得不删除object
从C类,使其工作。 (否则你会得到MRO问题)。
类型错误:调用元类基地时出错
无法创建一个一致的方法解析
顺序(MRO)为碱B,对象,A
然而,我的情况有点复杂。 在我的案例类C
是其中一台服务器 A
和B
将是在启动时加载的插件。 这些居住在自己的文件夹。
我也有一个名为类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
从基类A
和B
。 然而,这将使得当这些插件被独立运行,应避免他们旧式的对象(这应该是可能的,单元测试明智)
另一个简单的办法是消除object
从C
但是这也将使它成为一个旧式类和C.__bases__
将不可用,因此我不能额外的对象添加到基础C
什么将是一个很好的架构解决方案,你会怎么做这样的事? 现在我可以用旧式类插件本身生活。 但我宁可不使用它们。
想想这样说-你想要的混入,以覆盖某些行为的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()
我不知道的细节,所以我可能完全关闭基地在这里,但好像你正在使用错误的机制来实现你的设计。
首先,为什么是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
可能已经存在,且其行为不依赖于不管是被之前运行。 我知道你可能并不需要或者这两个属性的,但比你在简化的例子有它的几乎没有更多的代码,为什么打破什么类,如果你没有概念模型?
有一个简单的解决方法:创建一个辅助类,有一个好听的名字,像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?