Python将父类方法设为子类实例方法别名的原理疑问
问题描述
我目前正在阅读Luciano Ramalho的著作《Fluent Python》,在接口与继承相关章节中,我们构建了一个list的子类,我对其中一个实例方法的定义方式存在疑惑。简化后的示例如:
class ListWithLoadMethod(list): load = list.extend
这段代码生成了list的新子类,将extend方法别名为load。我们可以通过以下代码测试该类:
loaded_list = ListWithLoadMethod(range(4)) print(loaded_list) loaded_list.extend(range(3)) print(loaded_list) loaded_list.load(range(3)) print(loaded_list)
运行后得到预期输出:
[0, 1, 2, 3] [0, 1, 2, 3, 0, 1, 2] [0, 1, 2, 3, 0, 1, 2, 0, 1, 2]
我的疑惑来自类方法和静态方法的区别:ListWithLoadMethod是list的子类,在类定义时我们将load指向list.extend,Python是如何识别我们需要将load作为父类实例方法的别名,而不是指向list类的类方法的?
问题解答
核心原因是Python的描述符绑定机制,这是Python处理实例方法的底层规则,和类方法、静态方法的标记逻辑无关。
首先你要明确两个基础点:
- 定义在类中的普通实例方法本身就是实现了描述符协议的对象,
list.extend本身就是父类list的原生实例方法,没有被任何装饰器修改过。 - 你在子类
ListWithLoadMethod中写的load = list.extend,本质是给子类新增了一个类属性,直接指向父类的extend方法对象。
当你通过实例调用loaded_list.load(xxx)的时候,Python的属性查找逻辑会触发方法的描述符绑定:自动把当前实例loaded_list作为第一个参数self传入extend方法,所以行为和直接调用loaded_list.extend(xxx)完全一致,相当于只是给同一个方法起了个别名。
你疑惑的“为什么不会被当成类方法”其实很好理解:类方法需要显式用@classmethod装饰器包装,被装饰的方法会修改底层的描述符绑定逻辑,绑定时会把类对象作为第一个参数传入。而你这里直接引用的list.extend没有被修改过,自然只会走普通实例方法的绑定逻辑,不可能被识别为类方法。
你可以用下面的代码验证未绑定方法的逻辑:
test = ListWithLoadMethod() # 直接通过类调用load方法,需要手动传入实例作为第一个参数 ListWithLoadMethod.load(test, [4,5,6]) print(test) # 输出 [4,5,6]
内容的提问来源于stack exchange,提问作者Rami Luisto
相关产品推荐
相关产品推荐

