Python中带参数的super()函数有什么作用?各参数形式用途是什么?
super()的设计目的与适用场景 super()的核心逻辑是基于目标类的方法解析顺序(MRO)链,返回一个代理对象,帮你定位到MRO链中指定位置的类的成员,所有带参数的形式本质都是手动指定MRO的遍历起点和绑定上下文,和无参形式(编译器自动补全参数的语法糖)比起来灵活性更高。
1. 单参数形式:super(type)
设计目的
返回一个未绑定的super代理对象,仅记录MRO遍历的起点为type,不绑定任何实例或类。这个对象实现了描述符协议,当它作为类属性被实例访问时,会自动绑定到当前访问的实例上,效果和无参super()一致,但只需要在类定义阶段创建一次,不需要每次调用方法都重新生成super实例、计算MRO路径。
适用场景
- 框架、元类编程等需要高频复用父类调用逻辑的场景,减少重复创建super对象的性能开销;
- 需要动态绑定调用上下文的通用代理逻辑,比如批量给类注入方法时,提前定义好super代理,后续不同实例调用时自动完成绑定。
2. 类+实例双参数形式:super(type, obj)
设计目的
返回一个绑定到实例obj的super代理对象,要求isinstance(obj, type)必须成立。它会以type为起点,沿着obj所属类的MRO链往后找下一个类的成员,调用方法时会自动将obj作为第一个self参数传入,不需要手动传实例。
这个形式是Python 2时代标准的super写法,Python 3的无参super()在实例方法中,本质就是编译器自动补全了「当前所在类」和self两个参数的语法糖。
适用场景
- 需要手动跳过MRO链中中间类的场景,比如多重继承结构中,不想调用直接父类的实现,而是要调用更上层父类的方法;
- 兼容Python 2版本的代码;
- 外部已经拿到实例对象,需要从指定类位置开始调用父类方法的场景。
举个实际用例:
class A: def say(self): print("call A.say") class B(A): def say(self): print("call B.say") class C(B): def say(self): # 跳过直接父类B,直接调用MRO链中B之后的A的实现 super(B, self).say() C().say() # 输出:call A.say
3. 双类参数形式:super(type1, type2)
设计目的
返回一个绑定到类type2的super代理对象,要求issubclass(type2, type1)必须成立。它会以type1为起点,沿着type2的MRO链往后找下一个类的成员,拿到的是类级别的未绑定方法,需要手动传入实例才能调用实例方法,也可以直接调用类方法、静态方法或访问类属性。
这个形式不需要依赖任何类的实例,完全在类层面完成MRO定位。
适用场景
- 类方法、静态方法中调用父类的对应方法:Python 3无参
super()在类方法中,本质就是编译器自动补全了「当前所在类」和「当前类的元类实例(也就是类本身)」两个参数,等价于这个双类传参形式; - 元类编程场景:类创建阶段还没有生成任何类实例,需要调用父元类的逻辑(比如
__new__、__init__方法)时,只能用这个形式; - 不需要实例化类,直接在类层面获取父类的方法引用做工具函数使用的场景。
举个元类场景的用例:
class CustomMeta(type): def __new__(mcs, name, bases, namespace): # 类还未创建完成,不存在实例,用双类参数调用父元类的__new__ cls = super(CustomMeta, mcs).__new__(mcs, name, bases, namespace) cls.custom_tag = "added by meta" return cls
注意:不管哪种传参形式,
super()从来不是直接找「传入的第一个参数的父类」,而是找「第一个参数在第二个参数(或第二个参数所属类)的MRO链中的下一个类」,这是绝大多数super使用误区的来源。
内容的提问来源于stack exchange,提问作者Supergamer

