I am learning about design patterns in Python and wanted to combine the abstract factory with the delegation pattern (to gain deeper insights into how the pattern works). However, I am getting a weird recursion error when combining the two patterns, which I do not understand.
The error is:
[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
It is raised when client_with_laptop.display()
is called. However a/the recursion error is already stored in client_with_laptop._hardware
during the __init__
, although factory.get_hardware()
returns a laptop instance.
The code is:
from abc import abstractmethod
class ITechnique:
#abstract product
@abstractmethod
def display(self):
pass
def turn_on(self):
print("I am on!")
def turn_off(self):
print("I am off!")
class Laptop(ITechnique):
#concrete product
def display(self):
print("I'am a Laptop")
class Smartphone(ITechnique):
#concrete product
def display(self):
print("I'am a Smartphone")
class Tablet(ITechnique):
#concrete product
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()
When I access the attribute _hardware and remove the get_attribute section (so, basically, when I remove the delegation pattern), everything works as expected. See below the modified code section, which works:
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()
Can anybody help me explain why the recursion error occurs or how to fix it. My objective was (1) to have varying devices depending on the factory used in client and (2) to be able to call the methods from the _hardware
without typing client._hardware
all the time but calling it directly from the client object, e.g. client.display()
. It is not whether this is, in reality, a useful approach or not; I simply want to understand the pattern - and the occurring error - better. :-)
from abc import abstractmethod
class ITechnique:
#abstract product
@abstractmethod
def display(self):
pass
def turn_on(self):
print("I am on!")
def turn_off(self):
print("I am off!")
class Laptop(ITechnique):
#concrete product
def display(self):
print("I'am a Laptop")
class Smartphone(ITechnique):
#concrete product
def display(self):
print("I'am a Smartphone")
class Tablet(ITechnique):
#concrete product
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)
After you created a Client object, that object has the method __getattribute__. Inside this method you then proceed to call this object's __getattribute__ method when you access this objects display method. This causes instant recursion. To solve this you need to allow for figuring out what needs done. Read the documentation for __getattr__ and __getattribute__ to determine how you want to handle it.