如何在 Python 中正确结合抽象工厂模式与委托模式避免递归错误

1次阅读

如何在 Python 中正确结合抽象工厂模式与委托模式避免递归错误

本文详解为何在 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() 时:

  1. Python 调用 client.__getattribute__(‘display’);
  2. 方法内尝试读取 self._hardware → 触发 又一次 __getattribute__ 调用(因 _hardware 是实例属性);
  3. 新调用又试图读取 _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 “化身”为所委托的硬件对象,真正实现“一个接口,多种实现”的设计哲学。

text=ZqhQzanResources