Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/360.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何在不出现MRO错误的情况下动态地将mixin添加为基类?_Python_Class_Architecture_Method Resolution Order - Fatal编程技术网

Python 如何在不出现MRO错误的情况下动态地将mixin添加为基类?

Python 如何在不出现MRO错误的情况下动态地将mixin添加为基类?,python,class,architecture,method-resolution-order,Python,Class,Architecture,Method Resolution Order,假设我有一个类a,B和C class A( object ): pass class B( object ): pass class C( object, A, B ): pass 类A和B都是类C的混合类 class A( object ): pass class B( object ): pass class C( object, A, B ): pass 当实例化类C时,这将不起作用。我必须从类C中删除对象,才能使其起作用。(否则您将遇到

假设我有一个类
a
B
C

class A( object ):
    pass
class B( object ):
    pass
class C( object, A, B ):
    pass
A
B
都是类
C
的混合类

class A( object ):
    pass
class B( object ):
    pass
class C( object, A, B ):
    pass
当实例化类C时,这将不起作用。我必须从类C中删除
对象
,才能使其起作用。(否则您将遇到MRO问题)

TypeError:调用元类基时出错
无法创建一致的方法解析
基本B、对象、A的订单(MRO)

然而,我的情况有点复杂。在我的例子中,class
C
是一个服务器,
a
B
将是启动时加载的插件。它们位于自己的文件夹中

我还有一个名为
Cfactory
的类。在Cfactory中,我有一个
\uuuuuuuuuuuuuuuuu
方法,该方法将创建一个完全功能的对象C。在
\uuuuuuuuuuuuuuuuuuuuuu
方法中,我搜索插件,使用
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

因此,以下是一种可能性:(使其相当抽象)

这同样不起作用,并且会再次出现MRO错误:

TypeError:无法创建一致的方法解析
基本对象的订单(MRO),A

解决这个问题的一个简单方法是从
A
B
中删除
对象
基类。然而,这将使它们成为旧式对象,当这些插件独立运行时,应该避免使用这些对象(这应该是可能的,从单元测试的角度来看)

另一个简单的修复方法是从
C
中删除
object
,但这也会使它成为一个旧式类,并且
C.。\uuuuu-bases\uuuuuu
将不可用,因此我无法向
C
的基础添加额外的对象

对于这一点,什么是好的体系结构解决方案?您将如何做类似的事情?现在,我可以为插件本身使用老式的类。但是我宁愿不使用它们。

这样想——你希望mixin覆盖
object
的一些行为,所以它们必须在方法解析顺序中位于
object
之前

因此,您需要更改基的顺序:

class C(A, B, object):
    pass
由于,您需要
C
不直接从对象继承,以便能够正确地分配给
\uuuuuuuuuuuuu基
,工厂实际上可能只是一个函数:

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
是一个类,为什么它的
\uuuu new\uuu
方法返回其他对象的实例?这看起来是一种奇怪的方式来实现一个非常自然的函数
Cfactory
正如您所描述的(并显示了一个简化的示例),其行为根本不像类;您没有多个共享功能的实例(事实上,看起来您已经无法自然地构建它的实例)

老实说,
C
在我看来也不太像一个类。看起来您不能创建多个实例,否则您将得到一个不断增长的基础列表。因此,
C
基本上是一个模块,而不是一个类,只有额外的样板文件。我尽量避免使用“单实例类来表示应用程序或某个外部系统”模式(尽管我知道它很流行,因为Java要求您使用它)。但是类继承机制对于那些不是真正的类的东西,比如你的插件系统,通常是很方便的

我会使用
C
上的classmethod来查找和加载插件,插件由定义
C
的模块调用,以便插件始终处于良好状态。或者,您可以使用元类自动将它找到的任何插件添加到类库中。将配置类的机制与创建类实例的机制混为一谈似乎是错误的;这与灵活的解耦设计相反

如果在创建
C
时无法加载插件,那么我会在创建
C
实例之前,在可以搜索插件时手动调用configurator类方法

实际上,如果类不能在创建后立即进入一致状态,我可能更愿意创建动态类,而不是修改现有类的基础。这样系统就不会被锁定在一次配置一次实例化的类中;您至少可以使用多个实例加载不同的插件集。大概是这样的:

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

这样,创建
C
实例的单个调用将为您提供一个正确配置的实例,但它不会对可能已经存在的
C
的任何其他假设实例产生奇怪的副作用,并且其行为不取决于它以前是否运行过。我知道您可能不需要这两个属性中的任何一个,但它的代码量仅比您在简化示例中的代码多,如果不需要,为什么要打破类的概念模型呢?

有一个简单的解决方法:创建一个具有良好名称的助手类,如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 < /P>中几乎不存在Python 3中的旧样式类,所以最终你需要一个解决方案。是的,我尝试了你的第一个例子,它确实给出了这个bug(在编辑代码之前也找到了它)。让我来解决一些问题,看看它是否有效。@Daantimer是的,不幸的是bug混淆了正确的碱基顺序。哟