Python元类冲突/类型错误

时间:2016-07-15 19:29:54

标签: python python-2.7

我正在调试一个python脚本(python不是我的语言),这是我第一次在python中使用元类#s。当我运行下面的代码时,我遇到了元类冲突错误。

TypeError: Error when calling the metaclass bases
    metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

在尝试解决它时,我在MySQLMSoftware中注释掉了元类声明,认为它是多余的,因为它继承自具有相同元类声明的类,但这导致:

TypeError: Error when calling the metaclass bases
    module.__init__() takes at most 2 arguments (3 given)

非常感谢任何见解,指导或指导。我一直在阅读关于python的元类实现的问题,到目前为止这个问题还没有出现。

MSoftware.py

from abc import ABCMeta, abstractmethod

class MSoftware(object) :
    __metaclass__ = ABCMeta

    def __init__(self,name,spark=None,mysql=None):
        self.name = name
        ...

MySQLMSoftware.py

from mouse import Mouse, MSoftware
from abc import ABCMeta, abstractmethod

class MySQLMSoftware(MSoftware): # Traceback always goes here
    __metaclass__ = ABCMeta

    MAX_ROWS = 30000000

    def __init__(self,name,years,spark=None,mysql=None):
        MSoftware.__init__(self,name,spark,mysql)
        self.years = years
        ...

TTime.py

from mouse.Mouse import Mouse
from mouse.MySQLMSoftware import MySQLMSoftware

class TTime(MySQLMSoftware) :

    DATABASE = "Database"
    NAME = "table"
    YEARS = [2014,2016]

    def __init__(self,spark=None,mysql=None):
        MySQLMSoftware.__init__(self,TTime.NAME,TTime.YEARS,spark,mysql)
        ...

main.py

import sys
from mouse.Mouse import Mouse
from mouse.TTime import TTime

...

2 个答案:

答案 0 :(得分:4)

问题在于,当选择元类时,python会选择最继承的版本。但是,您有两个相互冲突的元类(ABCMetaMSoftware)。

我认为python3.x docs在这方面比python2.x文档更正确:

  

类定义的适当元类确定如下:

     
      
  • 如果没有给出基数且没有给出明确的元类,则使用类型()
  •   
  • 如果给出了显式元类,并且它不是type()的实例,则>然后它直接用作元类
  •   
  • 如果type()的实例作为显式元类,或base定义,那么使用最多派生的元类
  •   

一种解决方案是创建一个新的元类,它是两者的混合:

class NewMeta(type(MSoftware), ABCMeta):
    pass

在这种情况下,由于所有元类都是MSoftware的元类的“实例”,因此它将被选中NewMeta,因为它是派生最多的元类,并且事情应该有效(前提是{{ 1}}的元类可用于协同多继承)。

这是一个示例,我动态地创建MSoftware来解决一些虚拟类的问题:

NewMeta

答案 1 :(得分:3)

问题与您的导入有关,并且在同名的类和模块之间存在混淆。您在MSoftware中导入的MySQLMSoftware.pyMSoftware.py中实现的模块,而不是该模块中同名的类。要获得后者(不更改导入),您需要使用MSoftware.MSoftwareMouse类和模块可能存在类似的问题(更糟糕的是,因为顶级包也被命名为mouse。)

尝试更改行:

from mouse import Mouse, MSoftware

这两行:

from mouse.Mouse import Mouse
from mouse.MSoftware import MSoftware

这将修复元类冲突问题,并使MySQLMSoftware类中的元类声明变得不必要。

我想指出问题的根本原因(从更高层面来看)是模块设计不佳。您有几个模块,每个模块似乎包含一个与它们具有相同名称的类。这是其他语言(如Java)中常见的项目布局,但它在Python中并不是必需或不可取的。 <{1}}永远不是一个很好的理由。

可能你应该将大多数(或者全部)软件包的模块组合成少量文件,可能首先将大部分内容放在mouse.Mouse.Mouse(或顶级{{1}如果你把它们全部组合起来并且不再需要这个包了)。这样你就不会有太多的冗余导入。