MRO、装饰器的调用顺序——Python 中需要注意的细节.

1. 重载运算符的调用顺序

class Foo:
    def __init__(self, name):
        self._name = name

    def __str__(self):
        return self._name

    def __eq__(self, other):
        return other is self


class Bar(Foo):
    def __eq__(self, other):
        return str(self) == str(other)

class Foo1:
    def __init__(self, name):
        self._name = name

    def __str__(self):
        return self._name

    def __eq__(self, other):
        return str(self) == str(other)


foo = Foo("Hello")
bar = Bar("Hello")
foo1 = Foo1("Hello")

print(foo == bar)  # True, 调用了 bar 的 __eq__
print(bar == foo)  # True, 调用了 bar 的 __eq__
print(foo == foo1)  # False, 调用了 foo 的 __eq__
print(foo1 == foo)  # True, 调用了 foo1 的 __eq__

Python 中,如果 == 符号两端变量属于具有继承关系的类,则会优先调用子类的重载方法;如果 == 符号两端变量分别属于两个完全不同的类,则会调用左端类的重载进行判断(见代码中print部分的后两个)

如果一个没有重载,一个有重载,则会优先调用有重载的类的重载方法。

2. 多继承类的 MRO (Method Resolution Order)

class GrandParent:
    def __init__(self):
        print("Grandparent")

class Parent(GrandParent):
    def __init__(self):
        print("parent")

class Son(Parent, GrandParent): # 反过来会报错
    def __init__(self):
        super().__init__()
        print("son")

a = Son()
# parent
# son

参考文章:https://www.educative.io/edpresso/what-is-mro-in-python

这个参考文章比较详细,但是讲的有点复杂了。简单来说就是 CPython 在解析多个类继承的时候,会在继承的类里从左到右进行 DFS 查找,直到找到对应的方法为止。

3. 装饰器的调用顺序

print(10.1)
def func1(function):
    print(11.1)
    def func1_inter():
        print(11.2, func1_inter)
        function()  # func2_inter
        print(11.4)
        function()  # func2_inter
        print(11.5)
    print(11.6)
    return func1_inter

def func2(function):
    print(12.1)
    def func2_inter():
        print(12.2, func2_inter)
        function()  # my_func
        print(12.3)
        function()  # my_func
        print(12.5)
    print(12.6)
    return func2_inter

print(10.2)

@func1
@func2
def my_func():
    print(10.3)
print(10.4)
my_func()
print(10.5)

这个例子比较全面地展示了 Python 装饰器之间的调用关系。