Resolving metaclass conflicts
Solution 1
Instead of using the recipe as mentioned by jdi, you can directly use:
class M_C(M_A, M_B):
pass
class C(A, B):
__metaclass__ = M_C
Solution 2
Your example using sqlite3
is invalid because it is a module and not a class. 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
.
I used a variation of this activestate snippet using noconflict.py. The snippet needs to be reworked as it is not python 3.x compatible. 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.
Solution 3
This also happens when you try to inherit from a function and not a class.
Eg.
def function():
pass
class MyClass(function):
pass
Solution 4
To use the pattern described by @michael, but with both Python 2 and 3 compatibility (using the six
library):
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
Solution 5
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.
Yves Dorfsman
Updated on December 01, 2021Comments
-
Yves Dorfsman over 2 years
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:>>> 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
-
jdi almost 12 years@JBernardo: Thanks. I commented on it not being py3 compat. It has a few other issues in the snippet that make it not work as well.
-
TayTay over 7 yearsYou could use
class C(six.with_metaclass(MyMeta))
to make it python 3.x compatible, could you not? -
jdi over 7 years@Tgsmith61591 Maybe. I've never used the six library so I am not sure
-
Samuel Muldoon over 4 yearsIn python 2 you use that
__metaclass__
stuff. How would you do this in python 3? Would it work to simply haveclass C(A, B, metaclass = M_C)
-
Ark-kun over 4 yearsThat was it! I had the error when I inherited from a module instead of a class.
base_component
instead ofbase_component.BaseComponent
. -
theEpsilon about 4 years@SamuelMuldoon yeah, see my answer below
-
joshmcode almost 4 yearsI had imported my base class incorrectly into the derived class and this helped me see the silly mistake. Thanks.
-
user2561747 almost 4 yearsNo longer the case for python > 3.7; instead get
TypeError: function() argument 1 must be code, not str
-
user2561747 almost 4 yearsWhy doesn't python just do this for you? Seems like the metaclass version of multiple inheritance, which python handles very nicely for you.
-
Chickenmarkus almost 4 yearsThe method resolution order (MRO) can differ between the base and meta classes. Hence, python let you distinguish between the actual MROs. Thus, you have to take care that the new common meta class creates a valid new class from the base classes yourself.