101

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
4
  • 9
    sqlite3 is a module not a "class". Commented Jun 30, 2012 at 17:20
  • @agf: I was just looking at this and realized the same thing when you posted that. Commented Jun 30, 2012 at 17:25
  • Thanks agf, you're right! sqlite3.Connection makes it work. Commented Jun 30, 2012 at 18:13
  • Possible duplicate of Triple inheritance causes metaclass conflict... Sometimes Commented Feb 10, 2016 at 9:37

6 Answers 6

34

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
Sign up to request clarification or add additional context in comments.

2 Comments

In python 2 you use that __metaclass__ stuff. How would you do this in python 3? Would it work to simply have class C(A, B, metaclass = M_C)
@SamuelMuldoon yeah, see my answer below
23

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.

3 Comments

@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.
You could use class C(six.with_metaclass(MyMeta)) to make it python 3.x compatible, could you not?
@Tgsmith61591 Maybe. I've never used the six library so I am not sure
19

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.

Comments

18

This also happens when you try to inherit from a function and not a class.

Eg.

def function():
    pass

class MyClass(function):
    pass

4 Comments

That was it! I had the error when I inherited from a module instead of a class. base_component instead of base_component.BaseComponent.
I had imported my base class incorrectly into the derived class and this helped me see the silly mistake. Thanks.
No longer the case for python > 3.7; instead get TypeError: function() argument 1 must be code, not str
In fact, this is essentially what the problem was in OP's code (but with a module rather than a function).
11

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.

2 Comments

Why doesn't python just do this for you? Seems like the metaclass version of multiple inheritance, which python handles very nicely for you.
The 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.
8

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.