Python 3.x环境下如何实现点运算符与__getattribute__的一致重载?
Python 3.x 点运算符重载的一致性实现需求
基础前提
- 本次讨论仅基于Python 3.x及以上版本,不涉及Python 2.x的兼容场景
- 和多数编程语言规则一致,Python的点运算符用于实例的属性访问,例如对
Rectangle实例的属性取值操作:
# 创建Rectangle类的新实例 robby = Rectangle(3, 10) # 调用点运算符 x = robby.length
- 常规认知中,上述属性取值逻辑等价于
x = Rectangle.__getattribute__(robby, "length"),即点运算符的行为通常被认为由__getattribute__方法完全接管,但实际运行逻辑并非完全符合该认知。
魔法方法的特殊调用规则
Python中存在一类名称前后均以双下划线包裹的特殊方法,即魔法方法,典型示例为__len__()。
执行如下代码即可获取内置int类型支持的绝大多数魔法方法列表:
print("\n".join(filter(lambda s: s.startswith("__"), dir(int))))
输出的方法列表节选如下:
__abs__ __add__ __and__ __bool__ __ceil__ __class__ __delattr__ __dir__ __divmod__ __doc__ __eq__ __float__ [... 内容截断 ...] __rtruediv__ __rxor__ __setattr__ __sizeof__ __str__ __sub__ __subclasshook__ __truediv__ __trunc__ __xor__
重写__getattribute__的失效场景
实际开发中,自定义继承自object的类并重写object.__getattribute__时,自定义逻辑往往无法达到预期:Python会在部分场景下直接忽略重写后的点运算符逻辑,例如下述Klass类的实现:
class Klass: def __getattribute__(self, attr_name): return print obj = Klass() obj.append() # 可正常运行,obj.append == print obj.delete() # 可正常运行,obj.delete == print obj.length # 可正常运行 obj.x # 可正常运行 # 下述涉及魔法方法调用的逻辑均无法触发自定义的__getattribute__ # 下述代码等价于 x = Klass.__len__(obj) len(obj) # 等价于 x = Klass.__add__(obj, 5) x = obj + 5 # 等价于 x = Klass.__radd__(obj, 2) x = 2 + obj
核心实现目标
目前已知Python存在多种可重载点运算符的实现方案,我们需要筛选出可读性强、逻辑简洁、行为一致的实现方案:
- 行为一致的定义为:只要源码中使用点运算符获取属性,无论对应属性是普通方法还是魔法方法,都能触发自定义的点运算符逻辑
- 避免手动逐一为所有已知魔法方法编写实现,防止产生大量如下的样板冗余代码:
def __len__(*args, **kwargs): return getattr(args[0], "__len__")(*args, **kwargs)
说明:我们已经清楚
__getattr__与__getattribute__的设计区别,核心待解决的问题并非选择重写哪一个方法的问题。
内容的提问来源于stack exchange,提问作者Toothpick Anemone
相关产品推荐
相关产品推荐

