SoFunction
Updated on 2024-12-19

Python Multiple Inheritance Metaclass Conflict Problem Solving and Principle Exploration

contexts

Recently there is a need to customize a multi-inheritance with two parent abstract subclasses, easy to reuse most of the code for different modules, while mandatory to implement all the abstract methods, but did not want to take for granted the way to achieve multi-inheritance, but actually reported error metaclass conflict:

In [1]: import abc
In [2]: from  import admin
In [3]: class MyAdmin(, ):
   ...:     pass
   ...:
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-b159bc04ec1b> in <module>
----> 1 class MyAdmin(, ):
      2     pass
      3
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

A moment full of doubts, first through the search quickly found a solution, but did not understand the root cause of the problem and the principle of the solution, recently finally some time to explore some in-depth, here to record.
PS: All discussions in this article are based on Python3 and do not take into account some of the differences in Python2.

What is a metaclass?

It's important to first figure out what metaclass is before you can understand what metaclass conflict really means.

Analogies between ordinary class and metaclass

Here the relationship between class (class) and instance (instance) is used to explain by analogy, if you want to create a custom class A, and then create its instances, generally we will write so:

In [1]: class A:
    ...:     def test(self):
    ...:         print('call test')
In [2]: a = A()
In [3]: print(a, type(a))
<__main__.A object at 0x7f9f95414970> <class '__main__.A'>

As above, we have customized class A and generated an instance object a of class A. The output of the print statement shows that the type of instance a is exactly class A. At this point, if we further explore the type of A, we will find.

In [10]: print(type(A))
<class 'type'>

Type A is class type.
We would say that a is an instance of class A. Then by analogy we can say that class A is an instance of class type, or to put it another way: an instance of class A is a, and an instance of class type is A.
Now we try to define the metaclass:
In python class not only creates instance objects, it is also an object itself, ordinary class creates instance ordinary objects, metaclass (metaclass) creates instance class objects.
PS: Strictly speaking, the metaclass itself doesn't have to be a class, it can be any callable object that can return a class, so we won't go into that here.

Customizing and using metaclass

How to define a metaclass in python? In fact, a type is a metaclass. A type is the default metaclass for all classes, and all customized metaclasses will eventually use a type to perform the final work of creating a class.
In fact, the Python interpreter ends up calling type to create class A when it defines class A using the class A... syntax above. syntax, the Python interpreter ultimately creates class A by calling type, which is equivalent to the following code:

In [23]: def fn(self):
    ...:     print('call test')
    ...:
In [24]: A = type('A', (object, ), dict(test=fn))

type creates the signature of the class as follows:

type(name, bases, attrs)
name: To create theclassname (of a thing)
bases: Parent class to inherittuple(Can be null,butpython3customizableclassGenerally inherits by defaultobject)
attrs: incorporateclass定义属性name (of a thing)和值的dict

In the vast majority of cases we do not need to use metaclass, very few need to dynamically create/modify classes of complex scenarios such as Django's ORM need to use this technology. Here is a simple example of the use of metaclass, for example, we can simply create a class to the class unified plus its creation time of the metaclass, in order to meet the need to view the corresponding class can be created for the first time of this pseudo-need (only for the sake of citing this example and mention the demand _), the following AddCTimeMetaclass definition.

In [30]: from datetime import datetime
In [31]: class AddCTimeMetaclass(type):
    ...:     def __new__(cls, name, bases, attrs):
    ...:         attrs['ctime'] = ()
    ...:         return super().__new__(cls, name, bases, attrs)
    ...:

In [32]: class B(metaclass=AddCTimeMetaclass):
    ...:     pass
    ...:
In [33]: 
Out[33]: (2022, 10, 29, 1, 22, 46, 750176)

When defining class B, tell the interpreter to create class B not with the default type but with the custom metaclass AddCTimeMetaclass by specifying the metaclass parameter.

Clear meaning of metaclass confict (metaclass conflict)

After defining a metaclass and understanding how to use it, we can start to explore metaclass conflicts. The simplest example of a metaclass conflict is as follows:

In [42]: class M0(type):
    ...:     pass
    ...:
In [43]: class M1(type):
    ...:     pass
    ...:
In [44]: class A(metaclass=M0):
    ...:     pass
    ...:
In [45]: class B(metaclass=M1):
    ...:     pass
    ...:
In [46]: class C(A, B):
    ...:     pass
    ...:
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-46-9900d594feda> in <module>
----> 1 class C(A, B):
      2     pass
      3

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

As above M0 and M1 are custom metaclasses that serve as metaclasses for A and B respectively, something goes wrong when class C tries to multi-inherit A and B. Literally: the metaclasses of the subclasses must be (non-strict) subclasses of all of their base class metaclasses, and it looks like something goes wrong between multi-inheritance of normal classes and metaclass multi-inheritance something goes wrong.
How to understand this passage? We already know that A, B have their own metaclass M0, M1, so when C more inheritance A, B when C's metaclass should be M0 or M1? As M0, M1 there is no inheritance relationship between the two, with which can not be, Python does not know how to do, can only tell you out of the question.

prescription

So what exactly should the metaclass of C be ideally? Ideally it should be as follows:

M0     M1
 : \   / :
 :  \ /  :
 A  M2  B
  \  :  /
   \ : /
     C

That is, use M2 which inherits more than M0 and M1 as the metaclass of C. This is also the final solution to this problem, the specific code is as follows:

In [58]: class M2(M0, M1):
    ...:     pass
    ...:
In [59]: class C(A, B, metaclass=M2):
    ...:     pass
    ...:

As above we solve the metaclass conflict problem by manually defining M2 and manually explicitly specifying the metaclass of class C as M2.
At this point again back to the beginning of the encounter with multiple inheritance and the problems encountered when it is easy to understand: because of their own metaclass, at the same time, modelAdmin also has its own metaclass, and there is no inheritance relationship between the two, and thus class MyAdmin(, ) multiple inheritance when the interpreter can not deduce the metaclass to meet the conditions, naturally, the error report. The solution is the same as the above program, define a subclass of both metaclass and specify it as MyAdmin's metaclass, the code is as follows:

In [112]: print(type(), type())
<class ''> <class ''>
In [113]: class MyMeta(type(), type()):
     ...:     pass
     ...:
In [114]: class MyAdmin(, , metaclass=MyMeta):
     ...:     pass
     ...:
In [115]: print(type(MyAdmin))
<class '__main__.MyMeta'>

consultation

/wiki/1016959663602400/1017592449371072

/questions/100003/what-are-metaclasses-in-python

/JetpropelledSnake/p/

/~micheles/python/

This article on Python multiple inheritance metaclass conflict problem solving and the principle of exploration of the article is introduced to this, more related to Python multiple inheritance content, please search for my previous articles or continue to browse the following related articles I hope you will support me in the future!