我正在尝试为配置类实现单例模式。我有一个
_Config
类,它加载 config.yaml
文件。 Config
类是_Config
类的子类,每当创建Config
类的实例时,我希望它总是返回单个实例。 Config
类并没有按预期工作。错误显示在最后。
您能解释一下发生了什么事吗?错误从何而来?
这是内部
_Config
类:
import yaml
from loguru import logger
import os
def _load_config() -> dict:
logger.debug(f'Current work dir: {os.getcwd()}. Loading the config.yaml data...')
filename = 'config.yaml'
with open(filename, 'r') as f:
try:
return yaml.safe_load(f)
except yaml.YAMLError as exc:
logger.critical(exc)
# raise Exception(exc)
raise
class _Config(dict):
"""This class is an internal class not to be accessed directly. Use `Config` class instead."""
# def __new__(cls, *args, **kwargs):
def __init__(self, config: dict = None):
"""
By default, the Config class is initialized with the `config.yaml` file at the root directory, unless a `config`
parameter is provided.
:param config: a config dictionary to be initialized
"""
super().__init__()
self._config: dict = config or _load_config()
def __getattr__(self, name):
if name == '_config':
return self._config
if name not in self._config:
raise AttributeError(name)
item = self._config.get(name)
if item is None:
raise NotImplementedError(f'"{name}" not found in config.yaml')
if isinstance(item, dict):
item = _Config(config=item)
return item
def __getitem__(self, name):
return self.__getattr__(name)
以下是需要外部使用的
Config
类:
class Config(_Config):
"""
The `Config` class for the configs with a singleton instance. This class wraps the `_Config` class. \n
The reason to use a wrapper class to implement singleton pattern is that the class `_Config` itself cannot be
implemented as a singleton class. This is because in its `__getattr__` method, the `_Config` class may need to be
constructed:\n
`item = _Config(config=item)`
"""
_instance = None
def __new__(cls):
logger.debug('__new__')
if cls._instance is None:
logger.debug('Creating a new _Config instance')
cls._instance = super(Config, cls).__new__(cls)
#
return cls._instance
def __init__(self):
if not hasattr(self, '_config'):
logger.debug('no _config')
super().__init__()
但是该代码不起作用。运行
config1 = Config()
时,我得到以下输出:
2024-09-07 08:39:43.844 | DEBUG | config:__new__:66 - __new__
2024-09-07 08:39:43.844 | DEBUG | config:__new__:68 - Creating a new _Config instance
Ran 1 test in 0.158s
FAILED (errors=1)
Error
Traceback (most recent call last):
File "/opt/project/tests/test_config.py", line 22, in test_config
config1 = Config()
^^^^^^^^
File "/opt/project/config.py", line 74, in __init__
if not hasattr(self, '_config'):
^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/project/config.py", line 34, in __getattr__
item = self._config.get(name)
^^^^^^^^^^^^
File "/opt/project/config.py", line 34, in __getattr__
item = self._config.get(name)
^^^^^^^^^^^^
File "/opt/project/config.py", line 34, in __getattr__
item = self._config.get(name)
^^^^^^^^^^^^
[Previous line repeated 2983 more times]
RecursionError: maximum recursion depth exceeded
发生此错误的原因是代码中的无限递归。 当您初始化 Config 类
config1 = Config()
代码时,尝试检查 _config
属性是否存在。此检查触发调用 __getattr__
。但是在 __getattr__
中,你再次检查 _config
,我们就在这里,无限递归。
要解决此问题,您可以将 Config.init 更改为以下方式:
class Config():
...
def __init__(self):
if '_config' not in self.__dict__:
logger.debug('no _config')
super().__init__()