
本文介绍一种通过自定义 __getattr__ 并动态重绑定方法与属性访问,使包装类(如 B)能透明地覆盖被包装类(如 A)中 @property 和实例方法所依赖的属性值,从而改变其行为逻辑。
本文介绍一种通过自定义 `__getattr__` 并动态重绑定方法与属性访问,使包装类(如 `b`)能透明地覆盖被包装类(如 `a`)中 `@property` 和实例方法所依赖的属性值,从而改变其行为逻辑。
在 Python 中,当使用组合(wrapper)模式替代继承时,一个常见痛点是:被包装对象内部的方法仍会访问自身的属性,而非包装类提供的同名属性。例如,B 包装了 A 并重定义了 prop 属性,但调用 b.method() 时,A.method 内部的 self.prop 依然解析为 A.prop(返回 1),而非 B.prop(期望返回 2),导致结果为 2 而非预期的 4。
根本原因在于:__getattr__ 仅在属性未在 B 实例字典中找到时触发,而 A.method 是一个已绑定到 A 实例的方法,其内部 self 指向的是 A 对象,因此所有属性访问都发生在 A 的上下文中——B 的属性对它完全不可见。
要解决这一问题,关键在于 “劫持”对 A 方法和属性的访问,并将其执行环境切换到 B 实例。这可通过以下两步实现:
✅ 步骤一:重绑定实例方法(method)
利用 Python 的描述符协议,从 A 的方法中提取原始函数(__func__),再通过 __get__(self) 将其重新绑定到 B 实例,使方法内部的 self 指向 B,从而能访问 B.prop:
Python 环境工具专业版功能概述Python 环境工具专业版是一项面向实际任务的技能。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。核心要点使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。执行完成后建议检查结果是否符合预期,并保留必要记录。使用与执行同时注意输入完整性、执行条件和输出可用性。从功能定位来看,该技能
from inspect import ismethod
class B:
def __init__(self, a: A):
self._a = a
@property
def prop(self) -> int:
return 2
def __getattr__(self, attr):
value = getattr(self._a, attr)
if ismethod(value):
# 将 A 的方法重新绑定到 B 实例
return value.__func__.__get__(self)
return value
✅ 步骤二:代理属性访问(prop, other_prop 等)
仅重绑定方法还不够——若 A.other_prop 内部又依赖 self.prop,则需确保 other_prop 的 getter 也作用于 B。为此,应优先从 type(self._a) 获取属性描述符(如 property),并显式用 B 实例调用其 fget:
from inspect import ismethod
class B:
def __init__(self, a: A):
self._a = a
@property
def prop(self) -> int:
return 2
def __getattr__(self, attr):
# 1. 先检查 A 的类是否有 property 描述符
descriptor = getattr(type(self._a), attr, None)
if isinstance(descriptor, property):
return descriptor.fget(self) # 用 B 实例调用 getter
# 2. 否则获取 A 的属性值
value = getattr(self._a, attr)
# 3. 若是方法,则重绑定到 B
if ismethod(value):
return value.__func__.__get__(self)
return value
✅ 验证效果
a = A() b = B(a) print(b.prop) # → 2 print(b.method()) # → 4 (2 * 2) print(b.other_prop) # → 8 (2 * 4, 因为 other_prop.fget(b) → b.prop * 4)
⚠️ 注意事项
- 此方案要求
A中所有需被覆盖的属性均为@property(即property描述符),普通实例属性(如self._prop = 1)无法通过type(A).prop获取,故不适用。 -
__getattr__不处理__dunder__方法(如__str__),如需代理,需单独处理。 - 若
A的方法依赖其他非属性状态(如私有字段_cache),而B未提供对应逻辑,则可能引发错误——此方案仅解决“属性覆盖”,不替代完整行为模拟。 - 性能上存在轻微开销(每次属性访问需多层判断),但在多数业务场景中可忽略。
通过上述设计,B 在不修改 A、不使用继承、也不复制方法逻辑的前提下,实现了对 A 行为的上下文感知式覆盖——真正做到了“以包装之形,行继承之实”。










