实例分析Python3 MRO 和 super()

原文链接:https://www.linuxexam.net/2020/04/python3-mro-and-super.html

1 MRO vs DFLR

Attribute searching is the core concept for Python classes. The searching order is important as it determines which class's attributes are used ( the one first searched is used ).

 

1.1 DFLR is used for Python2's classic classes 

In Python2's classic classes, the searching method is called DFLR ( Depth First, Left to Right ). Let's take an example to make it easier to understand.

 

Classes B and C inherit from A, while class D inherits from B and C.

 

Figure 1

 

The DFLR order is D, B, A, C, A

Please note that A appears twice on the list.

1.2 MRO is used for Python3

In Python3, every class's most top parent must be 'object'. So if a class has more than one parent class, 'object' is always listed multiple times on DFLR. 

 

This will cause two issues:

  • The performance suffers from multiple scanning object class. 
  • The object class's attributes have higher priority than any other classes on the right.

To solve these issues, Python3 replaces DFLR with MRO. MRO stands for Method Resolution Order. Despite its name, MRO is used for any attributes besides methods.

 

MRO is a simple improvement to DFLR. It just removes all but the last occurrence of duplicated classes in the DFLR list. Let's still use the example in Figure 1.

 

The DFLR list is D, B, A, C, A

The corresponding MRO list is D, B, A, C, A.

MRO can be easily accessed by class.__mro__ or class.mro().

>>> class A: pass
...
>>> class B(A): pass
...
>>> class C(A): pass
...
>>> class D(B,C): pass
...
>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> D.mro()
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

2 super() can invoke non-superclass 

super() is a builtin provided by Python3 for users to utilize MRO. Usually, python progammers explicitly call parent class's method with class name.

 

cat super1.py

class A:

    def __init__(self):

        print('A.__init__()')

class B(A):

    def __init__(self):

        print("B.__init__()")

        A.__init__(self)

x = B()

 

python3 super1.py

B.__init__()

A.__init__()

 

This seems a little bit noisy. If class A changes its name, we have to modify two places in class B accordingly.

 

Like Java, Python3 provides a super() builtin for this.

 

cat super2.py

 

class A:

    def __init__(self):

        print('A.__init__()')

class B(A):

    def __init__(self):

        print("B.__init__()")

        super().__init__()

x = B()

 

Everything looks perfect now.

But super() is not that simple. It dynamically calculates the invoked method based on MRO, which means the super() here does NOT always invoke class A.  A real example would be much easier for us to understand what this means.

 

cat super3.py

#!/usr/bin/python3

 

class A:

    def __init__(self):

        print('A.__init__()')

 

class B(A):

    def __init__(self):

        print("B.__init__()")

        super().__init__()

 

class C(A):

    def __init__(self):

        print("C.__init__()")

 

class D(B,C): pass

 

obj = D()

 

python3 super3.py

B.__init__()

C.__init__()

 

Here we can see that the "super()" in class B actually invoked class C rather than its real superclass A.

 

"super()" here calculates the invoked class based on two facts, the class D's MRO and current class B.

 

class D's MRO is D, B, C, A, object.

super() will invoke the class after B in D's MRO, which is C.

 

super() actually accepts parameters as 

 

super(current_class, instance_object)

 

Inside it

  • gets the MRO of instance_object.__class__
  • gets the next class in MRO after current_class

 

cat super4.py

 

class A:

    def __init__(self):

        print('A.__init__()')

 

class B(A):

    def __init__(self):

        print("B.__init__()")

        super( __class__, self).__init__()

 

class C(A):

    def __init__(self):

        print("C.__init__()")

 

class D(B,C): pass

 

obj = D()

 

super(B, obj).__init__()

 

python3 super4.py

B.__init__()

C.__init__()

C.__init__()

 

3 super() is not that good

super() seems good at first look, but it can cause lots of issues. MRO order may not be what you really want when invoking a method.

 

More debates about super() can be found in the section "The super Built-in Function: For better or worse" of Learning Python (5th Edition).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值