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: t
As far as I understood from the previous answers the only think we usually have to do manually is:
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.
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
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
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.
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.
This also happens when you try to inherit from a function and not a class.
Eg.
def function():
pass
class MyClass(function):
pass