Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand the C _ 3 linearization algorithm and multiple inheritance in MRO's Python

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "how to understand the C3 linearization algorithm and MRO's Python multi-inheritance". The content of the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "how to understand C3 linearization algorithm and MRO's Python multi-inheritance".

Catalogue

What is MRO?

New-style Class vs. Old-style Class

Understand the MRO of old-style class

Understand the MRO of new-style class

C3 linearization algorithm

The method parsing order (Method Resolution Order, MRO) in Python defines the correct way for the Python interpreter to find function parsing when multiple inheritance exists. When the version of Python has developed from 2.2 to 2.3, and then to the current Python 3M MRO algorithm, corresponding changes have taken place. This change affects the way we program with different versions of Python in many cases.

What is MRO?

MRO full name method parsing order (Method Resolution Order). It defines the specific order in which the interpreter looks up the function parsing in the case of multiple inheritance in Python. What is the parsing order of functions? Let's start with a simple example. Please take a closer look at the following code:

Class A (): def who_am_i (self): print ("I am A") class B (A): pass class C (A): def who_am_i (self): print ("I am C") class D (BMagne C): pass d = D ()

If I asked whether calling d.who_am_i () using an instance of D in Python 2 would execute who_am_i () in An or who_am_i () in C, I think more than 90% of people would answer without thinking: it must be who_am_i () in C, because C is the direct parent of D. However, if you run the code with Python 2, you can see that d.who_am_i () prints I am A.

Do you think it's confusing and weird? It's right to feel strange!

Undefined

This example fully demonstrates the role of MRO: to determine the order in which functions in the base class should call functions in the parent class. It can be clearly said that with the development of Python, the MRO algorithm is no longer an algorithm that can be guessed by the execution results. If you don't delve into the details of the MRO algorithm, slightly more complex inheritance relationships and method calls can completely daze you.

New-style Class vs. Old-style Class

Before introducing different versions of the MRO algorithm, it is necessary to briefly review the history of the way classes are defined in Python. Although the old-fashioned class definition and MRO algorithm have been abolished in Python 3, for Python 2, which is still widely used, different ways of class definition are closely related to the MRO algorithm. Knowing this will help us move from Python 2 to Python 3 without inexplicable errors.

In Python 2.1 and before, we used to define a class like this (we call this class old-style class):

Class A: def _ _ init__ (self): pass

Python 2.2 introduces a new model object (new-style class), which recommends that new types be defined in the following ways:

Class A (object): def _ _ init__ (self): pass

Note that the latter definition shows that class An inherits from object. Python 2.3 and later versions provide the above two category definitions to distinguish between old-style class and new-style class in order to maintain backward compatibility. Python 3 completely abandons the concept of old-style class. No matter which way you write code, Python 3 will explicitly assume that class An inherits from object. We are only introducing the concepts of old-style and new-style here, and if you are interested in the difference between them, you can take a look at the explanation of the problem on stackoverflow.

Understand the MRO of old-style class

We use the class inheritance relationship in the previous article to introduce the MRO algorithm for old-style class in Python 2. If you executed that code earlier, you can see that the call to d.who_am_i () should print I am A. Why does the interpreter of Python 2 search for An instead of D's direct parent class C when determining function calls in D?

This is because Python 2 uses a very simple depth-first traversal-based MRO algorithm for old-style class (I'm sure you're all familiar with depth-first traversal). When a class inherits from multiple classes, Python 2 deeply traverses the inheritance graph of the class from left to right to determine the order in which the functions in the class are called. The process is as follows:

Check to see if the function is in the current class, and if so, call it directly.

Check whether the function exists in the first parent class of the current class, and if not, check whether the first parent class of the parent class has the function, thus recursively traversing the depth.

If not, go back one level, check to see if the function is in the next parent class and recurse as shown in 2.

The above process differs only slightly from the standard depth-first traversal: step 2 always selects the traversal order of branches according to the order of classes in the inheritance list. Specifically, the class order in the inheritance list of class D is B and C, so class D determines the MRO in the order that it traverses the B branch first and then the C branch.

Let's continue to use the function inheritance graph in the first example to illustrate this process:

According to the deep recursion above, the search order of the function d.who_am_i () call is D, B, A, C, A. Since a class cannot appear twice, the recurring An is removed in the search path, and the final method parsing order is D, B, A, C. So you can see why d.who_am_i () prints I am A.

In Python 2, we can view the MRO of old-style class as follows:

> import inspect > inspect.getmro (D) understand the MRO of new-style class

As you can see from the above results, the search algorithm using depth-first traversal is not reasonable. Therefore, Python 3 and Python 2 adopt a new MRO algorithm for new-style class. If you re-run the above script using Python 3, you can see that the printed result of the function d.who_am_i () is I am C.

> d.who_am_i () I am C > D.

The new algorithm is similar to the algorithm based on depth traversal, but the difference is that the new algorithm makes additional checks on the search path obtained by depth-first traversal. The search path obtained by scanning from left to right, for each node interpreter will determine whether the node is a good node or not. If it is not a good node, remove it from the current search path.

So the question is, what is a good node? We say that N is a good node if and only if the nodes after N in the search path do not inherit from N. We also take the above class inheritance graph as an example to get the search paths D, B, A, C, An of the functions in class D according to the depth-first traversal. Then the Python interpreter checks from left to right and finds that the third node An is not a good node because the node C after An inherits from A. So it removes A from the search path and gets the final call order D, B, C, A.

Using the above algorithm, the function call in D will first find the corresponding function in its direct parent class B and C.

C3 linearization algorithm

In the previous summary, we visually outlined the process of the MRO algorithm for new-style class. In fact, the algorithm has a clear name C3 linearization. Below, we give its formal calculation process.

The above process seems to be very complicated, let's use an example to implement it, you will think it is actually quite simple. Suppose we have a class inheritance relationship as follows:

Class X (): def who_am_i (self): print ("I am a X") class Y (): def who_am_i (self): print ("I am a Y") class A (X, Y): def who_am_i (self): print ("I am an A") class B (Y) X): def who_am_i (self): print ("I am a B") class F (A, B): def who_am_i (self): print ("I am a F")

Traceback (most recent call last): File "test.py", line 17, in class F (A, B): TypeError: Cannot create a consistent method resolutionorder (MRO) for bases X, Y Thank you for your reading. The above is the content of "how to understand C _ 3 linearization algorithm and multiple inheritance in MRO's Python". After the study of this article, I believe you have a deeper understanding of how to understand C _ 3 linearization algorithm and multiple inheritance in MRO's Python. The specific use situation still needs to be verified by practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report