python super与__mro__
简介
由于在开发中对于多重继承中的super方法不是很熟悉,本文将介绍super的使用场景,及super是根据什么去查找方法。
super([type[, object-or-type]])
- type:需要委托的父类或者兄弟类,就是调用哪个类,不写的话默认是父类
- object-or-type:确定用于搜索的 method resolution order。 搜索会从 type 之后的类开始。直白点就是调用的顺序。举例来说,如果 object-or-type 的
__mro__
为 D -> B -> C -> A -> object 并且 type 的值为 B,则 `super()`` 将会搜索 C -> A -> object。
object-or-type 的 mro 属性列出了 getattr() 和 super() 所共同使用的方法解析搜索顺序。 该属性是动态的,可以在任何继承层级结构发生更新的时候被改变。
官方术语:返回一个代理对象,它会将方法调用委托给 type 的父类或兄弟类。 这对于访问已在类中被重载的继承方法很有用。
正常话语:一般用于子类调用父类的方法
例如,有如下三个类
from inspect import getmro
class A(object):
def __init__(self) -> None:
pass
def run2(self):
print("run2 A")
def run(self):
print("run A")
class B(A):
def __init__(self) -> None:
super().__init__()
def run2(self):
print("run B")
class C(A):
def __init__(self) -> None:
super().__init__()
def run2(self):
print("run C")
当D调用B中的方法时
class D(B, C):
def __init__(self) -> None:
super().__init__()
def run2(self):
super(B, self).run2()
print(D.__mro__)
print(getmro(D))
D().run2()
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
run C
发现未显示B,显示的是C,由此可见super
查找的时候,是从type
后面去找,不算type
。
super使用场景
1.子类调用父类的方法,子类初始化父类的构造方法
class B(object):
def __init__(self, name) -> None:
self.name = name
def run(self):
print("B run")
class C(B):
def __init__(self, name, age) -> None:
super(C, self).__init__(name)
self.age = age
def run(self):
super().run()
print(f"name:{self.name}, age:{self.age}")
C("tom", 18).run()
B run
name:tom, age:18
2.出现在覆盖Python特殊方法的代码
class Proxy:
def __init__(self, obj):
self._obj = obj
# Delegate attribute lookup to internal obj
def __getattr__(self, name):
return getattr(self._obj, name)
# Delegate attribute assignment
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value) # Call original __setattr__
else:
setattr(self._obj, name, value)
在上面代码中,__setattr__()
的实现包含一个名字检查。 如果某个属性名以下划线(_)开头,就通过 super() 调用原始的 __setattr__()
, 否则的话就委派给内部的代理对象 self._obj 去处理。 这看上去有点意思,因为就算没有显式的指明某个类的父类, super()
仍然可以有效的工作。
Python继承的原理
例如存在如下代码
class A(object):
def __init__(self) -> None:
pass
def run(self):
print("A run")
class B(object):
def __init__(self) -> None:
pass
def run(self):
print("B run")
class C(A, B):
def __init__(self) -> None:
super().__init__()
def run(self):
super(C, self).run()
C().run()
print(C.__mro__)
A run
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
在python继承过程中,会基于MRO
列表从当前类的位置从左到右依次遍历检查类,直到找到符合要求的类,一版在查找过程中遵循以下准则:
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查, 就是从左向右,谁在前,谁先被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类, 同第二点,在一个类中找到了第一个属性则不会继续查找
除此之外,super()不仅仅可以使用在子类查找父类当中,也可以使用在非父类中,需要结合继承一起使用,super则就是基于继承的MRO
列表进行顺序查找,如下:
class A(object):
def __init__(self) -> None:
pass
def run(self):
print("A run")
super().run()
A().run()
A run
Traceback (most recent call last):
File "c:\Users\ts\Desktop\2022.7\2022.8.5\test.py", line 88, in <module>
A().run()
File "c:\Users\ts\Desktop\2022.7\2022.8.5\test.py", line 85, in run
super().run()
AttributeError: 'super' object has no attribute 'run'
由结果可知,当一个单独的类调用super时会直接报错,但使用如下代码进行调用时则不会报错
class A(object):
def __init__(self) -> None:
pass
def run(self):
print("A run")
super().run()
class B(object):
def __init__(self) -> None:
pass
def run(self):
print("B run")
class C(A, B):
def __init__(self) -> None:
super().__init__()
c = C()
c.run()
print(C.__mro__)
A run
B run
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
C在调用A的run
方法时, 其中A中的super()方法调用的是B中的方法,这是为什么呢,其实从C的mro列表中可以看出,B在A的后面,super不管是在子类还是父类中都遵循mro列表中的顺序依次查找属性或者方法,直到找到所需的。
总结
- super()不仅可以出现在子类使用父类的方法中,也可以出现在父类当中
- super()的调用过程遵循当前调用类的
MRO列表
,依据当前调用类的位置,从左向右依次遍历检查,直到找到所需的内容
补充
mro列表的获取
C.__mro__
from inspect import getmro
getmro(C)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现