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!