说我有一个课程A
,B
和C
。
班级A
和B
都是班级C
的混合班级。
class A( object ):
pass
class B( object ):
pass
class C( object, A, B ):
pass
在实例化类C时这不起作用。我必须从类C中删除object
才能使它工作。 (否则你会遇到MRO问题)。
TypeError:调用元类库时出错 无法创建一致的方法解决方案
基数B,对象,A的订单(MRO)
然而,我的情况有点复杂。在我的情况下,类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错误:
TypeError:无法创建一致的方法分辨率 基础对象的订单(MRO),A
一个简单的解决方法是从object
和A
删除B
基类。然而,这将使它们成为旧式对象,当这些插件独立运行时应该避免(这应该是可能的,UnitTest明智的)
另一个简单的解决方法是从object
删除C
,但这也会使它成为旧式类,C.__bases__
将不可用,因此我无法向基础添加额外的对象C
对于这个有什么好的架构解决方案,你会怎么做这样的事情?现在我可以使用插件本身的旧式类。但我宁愿不使用它们。
答案 0 :(得分:11)
以这种方式思考 - 您希望mixins覆盖object
的某些行为,因此它们需要在方法解析顺序中位于object
之前。
所以你需要改变基数的顺序:
class C(A, B, object):
pass
由于this bug,您需要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()
答案 1 :(得分:4)
我不知道细节,所以也许我完全偏离了这里,但看起来你正在使用错误的机制来实现你的设计。
首先,为什么Cfactory
是一个类,为什么它的__new__
方法返回其他东西的实例?这看起来像是一种奇怪的方式来实现一个很自然的功能。正如你所描述的那样Cfactory
(并显示了一个简化的例子)并不像一个类那样表现;你没有多个共享功能的实例(实际上看起来你已经无法自然地构造实例了)。
说实话,C
对我来说看起来也不像是一堂课。看起来你不能创建它的多个实例,否则你最终会得到一个不断增长的基础列表。因此,C
基本上是一个模块而不是一个类,只有额外的样板。我试图避免“单实例类来表示应用程序或某些外部系统”模式(虽然我知道它很受欢迎,因为Java 需要你使用它)。但是类继承机制通常可以用于非真正类的事情,例如你的插件系统。
我会在C
上使用classmethod来查找和加载插件,由定义C
的模块调用,以便始终处于良好状态。或者,您可以使用元类自动将它找到的任何插件添加到类库中。将用于配置类的机制与用于创建类实例的机制混合似乎是错误的;它与柔性解耦设计相反。
如果在创建C
时无法加载插件,那么我会在您可以搜索插件之前手动调用配置器类方法。 <{1}}实例已创建。
实际上,如果类一旦创建就无法进入一致状态,我宁可去创建动态类而不是修改现有类的基础。然后系统不会被锁定到被配置一次的类并实例化一次;你至少可以开始使用不同的插件集加载多个实例。像这样:
C
这样,您可以通过单次调用来创建def Cfactory(*args, **kwargs):
plugins = find_plugins()
bases = (C,) + plugins
cls = type('C_with_plugins', bases, {})
return cls(*args, **kwargs)
实例,并为您提供正确配置的实例,但它对C
的任何其他假设实例没有奇怪的副作用已存在,其行为并不取决于它是否已经运行过。我知道你可能不需要这两个属性中的任何一个,但它只是你的简化示例中的代码,并且为什么要破坏你不需要的类的概念模型?
答案 2 :(得分:3)
有一个简单的解决方法:创建一个名为helper-class的类,如 PluginBase 。并使用继承而不是对象。
这使得代码更具可读性(imho),并且它存在错误。
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中几乎没有。