简体   繁体   English

解决元类冲突

[英]Resolving metaclass conflicts

I need to create a class that uses a different base class depending on some condition.我需要创建一个根据某些条件使用不同基类的类。 With some classes I get the infamous:在一些课程中,我得到了臭名昭著的:

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

One example is sqlite3 , here is a short example you can even use in the interpreter:一个例子是sqlite3 ,这是一个你甚至可以在解释器中使用的简短例子:

>>> import sqlite3
>>> x = type('x', (sqlite3,), {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

Instead of using the receipe as mentioned by jdi, you can directly use:您可以直接使用:

class M_C(M_A, M_B):
    pass

class C(A, B):
    __metaclass__ = M_C

Your example using sqlite3 is invalid because it is a module and not a class.您使用sqlite3示例无效,因为它是一个模块而不是一个类。 I have also encountered this issue.我也遇到过这个问题。

Heres your problem: The base class has a metaclass that is not the same type as the subclass.这是您的问题:基类具有与子类不同类型的元类。 That is why you get a TypeError .这就是你得到TypeError

I used a variation of this activestate snippet using noconflict.py .使用 noconflict.py使用了这个 activestate 片段的变体。 The snippet needs to be reworked as it is not python 3.x compatible.该代码段需要重新编写,因为它与 python 3.x 不兼容。 Regardless, it should give you a general idea.无论如何,它应该给你一个大致的概念。

Problem snippet问题片段

class M_A(type):
    pass
class M_B(type):
    pass
class A(object):
    __metaclass__=M_A
class B(object):
    __metaclass__=M_B
class C(A,B):
    pass

#Traceback (most recent call last):
#  File "<stdin>", line 1, in ?
#TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass #of the metaclasses of all its bases

Solution snippet解决方案片段

from noconflict import classmaker
class C(A,B):
    __metaclass__=classmaker()

print C
#<class 'C'>

The code recipe properly resolves the metaclasses for you.代码配方为您正确解析元类。

This also happens when you try to inherit from a function and not a class.当您尝试从函数而不是类继承时,也会发生这种情况。

Eg.例如。

def function():
    pass

class MyClass(function):
    pass

To use the pattern described by @michael, but with both Python 2 and 3 compatibility (using the six library):要使用@michael 描述的模式,但同时兼容 Python 2 和 3(使用six库):

from six import with_metaclass

class M_C(M_A, M_B):
    pass

class C(with_metaclass(M_C, A, B)):
    # implement your class here

As far as I understood from the previous answers the only things we usually have to do manually are:据我从之前的答案中了解到,我们通常需要手动做的唯一事情是:

class M_A(type): pass
class M_B(type): pass
class A(metaclass=M_A): pass
class B(metaclass=M_B): pass

class M_C(M_A, M_B): pass
class C:(A, B, metaclass=M_C): pass

But we can automate the last two lines now by:但是我们现在可以通过以下方式自动化最后两行:

def metaclass_resolver(*classes):
    metaclass = tuple(set(type(cls) for cls in classes))
    metaclass = metaclass[0] if len(metaclass)==1 \
                else type("_".join(mcls.__name__ for mcls in metaclass), metaclass, {})   # class M_C
    return metaclass("_".join(cls.__name__ for cls in classes), classes, {})              # class C

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

Since we do not use any version-specific metaclass syntax this metaclass_resolver works with Python 2 as well as Python 3.由于我们不使用任何特定于版本的元类语法,因此metaclass_resolver适用于 Python 2 和 Python 3。

I like doing:我喜欢做:

class mBase1(type):
    ...

class mBase2(type):
    ...

class Base1(metaclass=mBase1):
    ...

class Base2(metaclass=mBase2):
    ...

class mChild(type(Base1), type(Base2)):
    pass

class Child(Base1, Base2, metaclass=mChild):
    ...

That way if something changes with the metaclass of the bases you don't have to worry about it.这样,如果基类的元类发生变化,您就不必担心。 type() will take care of it. type()会处理它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM