抽象工厂模式与委托模式结合出现RecursionError的原因及解决方法
问题:结合抽象工厂与委托模式时触发RecursionError
我在Python中学习设计模式,尝试结合抽象工厂模式与委托模式加深理解,但遇到了无法解决的RecursionError,错误信息如下:
[Previous line repeated 987 more times] File "c:\Users\jenny\Documents\design_pattern\creational\abstract_factory.py", line 60, in __getattribute__ def __getattribute__(self, name: str): RecursionError: maximum recursion depth exceeded
该错误在调用client_with_laptop.display()时触发,甚至在__init__阶段,client_with_laptop._hardware就已经存在递归问题。
相关完整代码如下:
from abc import abstractmethod class ITechnique: # 抽象产品 @abstractmethod def display(self): pass def turn_on(self): print("I am on!") def turn_off(self): print("I am off!") class Laptop(ITechnique): # 具体产品 def display(self): print("I'am a Laptop") class Smartphone(ITechnique): # 具体产品 def display(self): print("I'am a Smartphone") class Tablet(ITechnique): # 具体产品 def display(self): print("I'm a tablet!") class IFactory: @abstractmethod def get_hardware(): pass 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 __getattribute__(self, name: str): return getattr(self._hardware, name) if __name__ == "__main__": client_with_laptop = Client(LaptopFactory()) client_with_laptop.display() client_with_tablet = Client(TabletFactory()) client_with_tablet.display() client_with_smartphone = Client(SmartphoneFactory()) client_with_smartphone.display()
移除__getattribute__(委托模式实现部分)后,直接访问_hardware代码可正常运行,修改后的代码片段:
class Client(): def __init__(self, factory: IFactory) -> None: self._hardware = factory.get_hardware() if __name__ == "__main__": client_with_laptop = Client(LaptopFactory()) client_with_laptop._hardware.display() client_with_tablet = Client(TabletFactory()) client_with_tablet._hardware.display() client_with_smartphone = Client(SmartphoneFactory()) client_with_smartphone._hardware.display()
我的目标是:
- 根据Client使用的工厂生成对应设备;
- 无需输入
client._hardware,直接通过Client对象调用_hardware的方法(如client.display())。
错误原因
问题出在Client类的__getattribute__方法上:
- Python中,访问任何实例属性或方法都会触发
__getattribute__,包括你在方法内部访问self._hardware的时候。 - 当你在
__getattribute__里写self._hardware,会再次调用当前类的__getattribute__方法,试图获取_hardware属性,这就形成了无限递归,最终触发递归深度超限错误。
修复方法
有两种可靠的修复方式,都能实现你要的委托效果:
方法1:使用super().__getattribute__获取_hardware
通过调用父类(object)的__getattribute__方法来获取_hardware,绕开当前类的递归调用:
class Client(): def __init__(self, factory: IFactory) -> None: self._hardware = factory.get_hardware() def __getattribute__(self, name: str): # 先通过父类方法获取_hardware,避免递归 hardware = super().__getattribute__('_hardware') return getattr(hardware, name)
方法2:改用__getattr__方法
__getattr__只会在访问的属性/方法不存在于当前实例时才会被调用,不会影响已有属性(比如_hardware)的正常访问,天然避免递归问题:
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)
两种方法都能让你直接通过client.display()调用设备的方法,同时保持抽象工厂模式的功能。
内容的提问来源于stack exchange,提问作者JKupzig
相关产品推荐
相关产品推荐

