
本文详解为何在 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 “化身”为所委托的硬件对象,真正实现“一个接口,多种实现”的设计哲学。










