Python 单例实现不起作用

问题描述 投票:0回答:1

我正在尝试为配置类实现单例模式。我有一个

_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
python inheritance runtime-error singleton
1个回答
0
投票

发生此错误的原因是代码中的无限递归。 当您初始化 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__()
© www.soinside.com 2019 - 2024. All rights reserved.