I'm trying to implement a Config class that holds and keeps track of an arbitrary amount of Settings. I'd like to get and set the Settings like c = Config(<CONTAINER_OF_POSSIBLE_SETTINGS>); c.x = 1; print(c.foo) where x and foo are some Settings (or their name attribute) given to the Config during initialization. So I tried overwriting the __setattr__ and __getattr__ methods. But I'm running into an infinite recursion problem. Here is the bare-bones version of what I have so far:
#config.py
class Setting:
def __init__(self, name, value):
self.name = name
self.value = value
def __repr__(self) -> str:
return f"Setting(name: {self.name}, value: {self.value})"
class Config:
#_settings = {}
def __init__(self, settings):
self._settings = settings
def __setattr__(self, name, value):
try:
self._settings[name].value = value
except KeyError as err:
try:
return super(Config, self).__setattr__(name, value)
except AttributeError:
raise err
def __getattr__(self, name):
try:
return self._settings[name].value
except KeyError as err:
try:
return super(Config, self).__getattribute__(name)
except AttributeError:
raise err
class Interface:
def __init__(self):
settings = {
s.name: s for s in [
Setting("x", 1.),
Setting("y", 2.),
]
}
self.config = Config(settings)
if __name__ == "__main__":
i = Interface()
print(i.config._settings)
i.config.x = 9.
print(i.config._settings)
#print(Config._settings)
The idea was that __setattr__ would try to look up the attribute name in the self._settings dictionary or - if it can't find a Setting with the name - set the attribute the "usual way" by calling super().__setattr__.
But running this results in:
$ python config.py
Traceback (most recent call last):
File "config.py", line 52, in <module>
i = Interface()
File "config.py", line 48, in __init__
self.config = Config(settings)
File "config.py", line 15, in __init__
self._settings = settings
File "config.py", line 20, in __setattr__
self._settings[name].value = value
File "config.py", line 31, in __getattr__
return self._settings[name].value
File "config.py", line 31, in __getattr__
return self._settings[name].value
File "config.py", line 31, in __getattr__
return self._settings[name].value
[Previous line repeated 991 more times]
RecursionError: maximum recursion depth exceeded
From what I read in other threads, the problem is that self._settings in Config.__init__() calls __setattr__ which in turn calls __getattr__. And both methods try to access self._settings which causes the recursion. I'm not even getting to part where I try to access the dictionary's items.
One "hot-fix" I found was adding _settings as a class attribute to Config. After un-comenting the two lines in the code above the program yields:
$ python config.py
{'x': Setting(name: x, value: 1.0), 'y': Setting(name: y, value: 2.0)}
{'x': Setting(name: x, value: 9.0), 'y': Setting(name: y, value: 2.0)}
{}
But now I have this empty class attribute which is not what I want. And it shows that I don't fully understand initialization, __setattr__ , and __getattr__.
What I'm trying to do is telling Config that the _settings attribute should be handled differently. Unfortunately, I can't just have all the Settings be class attributes of Config since they (can) depend on the Config and/or Interface class.
Is there any way to do what I'm trying to do? Thanks in advance!