
本文详解为何在 Client 类中重写 __getattribute__ 会导致无限递归,以及如何通过改用 __getattr__ 安全实现委托式方法代理,从而无缝整合抽象工厂与委托模式。
本文详解为何在 `client` 类中重写 `__getattribute__` 会导致无限递归,以及如何通过改用 `__getattr__` 安全实现委托式方法代理,从而无缝整合抽象工厂与委托模式。
在 python 设计模式实践中,将抽象工厂模式(用于创建一族相关对象)与委托模式(用于将方法调用转发给内部对象)结合,是一种理解对象协作机制的有力方式。但若实现不当,极易触发 RecursionError——正如示例中 client_with_laptop.display() 调用时出现的“maximum recursion depth exceeded”错误。
问题根源在于对 __getattribute__ 的误用。该方法是 Python 属性访问的通用拦截器:每次访问任意属性(包括 self._hardware、self.__dict__,甚至方法本身如 display)时,都会无条件调用它。在原始代码中:
def __getattribute__(self, name: str): return getattr(self._hardware, name) # ❌ 问题在此
当执行 client.display() 时:
- Python 调用 client.__getattribute__(‘display’);
- 方法内尝试读取 self._hardware → 触发 又一次 __getattribute__ 调用(因 _hardware 是实例属性);
- 新调用又试图读取 _hardware → 再次触发……形成死循环,直至栈溢出。
✅ 正确解法:使用 __getattr__ 替代 __getattribute__。
立即学习“Python免费学习笔记(深入)”;
__getattr__ 仅在常规属性查找失败后(即该属性不在实例字典、类字典或继承链中) 才被调用,因此它是实现“委托缺失方法”的安全钩子。修改后的 Client 类如下:
class Client: def __init__(self, factory: IFactory) -> None: self._hardware = factory.get_hardware() # ✅ 私有委托对象 def __getattr__(self, name: str): # 仅当 client 自身没有该属性时,才委托给 _hardware return getattr(self._hardware, name)
完整可运行示例(已修复):
from abc import ABC, abstractmethod class ITechnique(ABC): @abstractmethod def display(self): ... def turn_on(self): print("I am on!") def turn_off(self): print("I am off!") class Laptop(ITechnique): def display(self): print("I'm a Laptop") class Smartphone(ITechnique): def display(self): print("I'm a Smartphone") class Tablet(ITechnique): def display(self): print("I'm a tablet!") class IFactory(ABC): @abstractmethod def get_hardware(self): ... class SmartphoneFactory(IFactory): def get_hardware(self): return Smartphone() class LaptopFactory(IFactory): def get_hardware(self): return Laptop() class TabletFactory(IFactory): def get_hardware(self): return Tablet() class Client: def __init__(self, factory: IFactory) -> None: self._hardware = factory.get_hardware() def __getattr__(self, name: str): return getattr(self._hardware, name) # ✅ 测试:直接调用,无需 ._hardware 后缀 if __name__ == "__main__": client_with_laptop = Client(LaptopFactory()) client_with_laptop.display() # 输出: I'm a Laptop client_with_laptop.turn_on() # 输出: I am on! client_with_tablet = Client(TabletFactory()) client_with_tablet.display() # 输出: I'm a tablet!
⚠️ 注意事项:
- 永远避免在 __getattribute__ 中直接访问 self.xxx(除非显式调用 Object.__getattribute__(self, name)),否则必然引发递归;
- __getattr__ 是委托模式的黄金标准,语义清晰且安全;
- 若需同时控制存在/不存在属性的行为(如日志、缓存),应优先考虑 __getattribute__ + super().__getattribute__(name) 模式,而非裸写 getattr(self, name);
- 抽象工厂确保了 Client 的硬件类型可配置,而 __getattr__ 实现了透明委托——二者协同,既保持了松耦合,又提供了简洁接口。
总结:设计模式的组合不是简单拼接,而是需深入理解其底层机制(如 Python 数据模型)。用对 __getattr__,就能优雅地让 Client “化身”为所委托的硬件对象,真正实现“一个接口,多种实现”的设计哲学。