Python中X.__getitem__(1)与type(X).__getitem__(X,1)调用差异问题
Python属性查找差异原理分析
首先整理示例代码如下:
class C: data = 'spam' def __getattr__(self, name): print('getattr:', name) return getattr(self.data, name) X = C() X.__getitem__(1) # <-- 正常运行 type(X).__getitem__(X, 1) # <-- 抛出AttributeError: type object 'C' has no attribute '__getitem__'
两种调用的本质差异
- 调用
X.__getitem__(1)时,属性查找的主体是实例X,走实例的普通属性查找流程 - 调用
type(X).__getitem__(X, 1)时,属性查找的主体是类C,走类的属性查找流程,和实例X的属性逻辑无关
第一种调用正常运行的原因
实例属性查找的完整顺序为:
- 检查实例自身的
__dict__中是否存在__getitem__属性,不存在 - 检查实例所属类C及其父类的
__dict__中是否存在__getitem__属性,不存在 - 触发实例绑定的
__getattr__方法,返回data属性的__getitem__方法,调用后正常得到结果。
第二种调用抛出异常的原因
类属性查找的完整顺序为:
- 检查类C自身的
__dict__中是否存在__getitem__属性,不存在 - 检查类C的父类(默认继承
object)的__dict__中是否存在__getitem__属性,不存在 - 此时会触发类C自身的
__getattr__方法,而你代码中只给实例定义了__getattr__,类C本身没有该方法,因此直接抛出AttributeError。
补充:普通类中定义的
__getattr__仅作用于该类的实例对象,若要自定义类层面的缺失属性处理逻辑,需要给类的元类定义__getattr__方法。
关于显式调用特殊方法的疑问
你提到的隐式调用特殊方法(如X[1])跳过__getattr__是另一套规则:Python解释器处理运算符对应的隐式特殊方法调用时,会直接从类层面开始查找特殊方法,不会走实例的普通属性查找流程,因此也不会触发实例的__getattr__。而本次你遇到的两种显式调用的差异,本质是属性查找的主体不同,和特殊方法的隐式调用规则无关。
内容的提问来源于stack exchange,提问作者Goddrew
相关产品推荐
相关产品推荐

