带构造参数的Python单例实现报错及逻辑疑问
带构造参数的单例实现问题与解决
问题描述
尝试实现支持构造参数的单例模式,但传递参数给元类时触发错误,同时对现有逻辑的有效性存在疑问:控制台显示元类的__init__和__call__先于MySingleton构造函数执行,输入参数被用于__call__中实例化最终单例self._myObject。
原代码
from typing_extensions import List, TypedDict, Optional, Any class Singleton(type): # 继承type以获取__call__方法 _var: str = None _myObject: Any = None # 原代码中MyType未定义,此处改为Any def __init__(self, *args, **kwargs): print(f"Singleton::__init__") print(f"Singleton::__init__ len(kwargs): {len(kwargs)}, len(args): {len(args)}, args[0]: {args[0]}") print(**kwargs) for i in args: print(f"i: {i}") if "var" in kwargs and kwargs.get("var"): self._var = kwargs.get('var') elif len(args): self._var = args[0] else: self._var = "text-embedding-005" print(f"Singleton::__init__self._var: {self._var}") self.__instance = None # 存储实例引用的变量 super().__init__(*args, **kwargs) def __call__(self, *args, **kwargs): if self.__instance is None: # 对象尚未创建时 self._myObject = self._var # 原代码MyType未定义,此处调整 print(f"Singleton::__call__ self._var: {self._var}") self.__instance = super().__call__(*args, **kwargs) return self.__instance else: # 对象已存在时返回引用 return self.__instance class MySingleton(metaclass=Singleton): """ 类构造函数 """ def __init__(self, *args, **kwargs): print(f"MySingleton::__init__") super().__init__(*args, **kwargs) print(f"MySingleton::__init__ {self._var}") myobject = MySingleton("hello") myobject1 = MySingleton("world")
错误信息
Singleton::__init__ Singleton::__init__ len(kwargs): 0, len(args): 3, args[0]: MySingleton i: MySingleton i: () i: {'__module__': '__main__', '__qualname__': 'MySingleton', '__doc__': '\n Class constructor\n', '__init__': <function MySingleton.__init__ at 0x7de2ca4fd800>, '__classcell__': <cell at 0x7de2ca5399f0: Singleton object at 0x12d21760>} Singleton::__init__self._model: MySingleton Singleton::__call__ _model: MySingleton MySingleton::__init__ Traceback (most recent call last): File "singleton.py", line 43, in <module> vector_store = MySingleton("hello") ^^^^^^^^^^^^^^^^^^^^ File "singleton.py", line 26, in __call__ self.__instance = super().__call__(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "singleton.py", line 38, in __init__ super().__init__(*args, **kwargs) TypeError: object.__init__() takes exactly one argument (the instance to initialize)
问题分析
- 直接错误原因:
MySingleton默认继承自object,其__init__调用super().__init__(*args, **kwargs)时传入了额外参数,但object.__init__仅接受self一个参数,触发类型错误。 - 元类逻辑错误:元类的
__init__方法在定义MySingleton类时执行,其args是类定义的核心参数(类名、基类元组、类命名空间),并非实例化时传入的"hello"。原代码错误地将类定义参数当作实例化参数处理,导致_var被赋值为MySingleton类本身。 - 单例逻辑有效性问题:元类
__init__仅在类创建时运行一次,无法获取实例化阶段的参数,原代码试图在此处处理实例化参数的逻辑完全错误。
修正后的实现
from typing import Any class Singleton(type): _instance: Any = None _init_args: tuple = () _init_kwargs: dict = {} def __call__(self, *args, **kwargs): if self._instance is None: # 保存第一次实例化的参数 self._init_args = args self._init_kwargs = kwargs self._instance = super().__call__(*args, **kwargs) else: # 后续实例化忽略新参数(可改为抛出错误) if args != self._init_args or kwargs != self._init_kwargs: print(f"警告:单例已存在,将忽略新参数 {args}, {kwargs}") return self._instance class MySingleton(metaclass=Singleton): def __init__(self, var: str): print(f"MySingleton::__init__ 传入参数: {var}") self._var = var # 测试 myobject = MySingleton("hello") print(f"myobject._var: {myobject._var}") myobject1 = MySingleton("world") print(f"myobject1._var: {myobject1._var}") print(f"是否为同一实例: {myobject is myobject1}")
修正说明
- 移除元类中错误的
__init__逻辑,将参数处理移至__call__方法(实例化类时触发)。 - 保存第一次实例化的参数,后续实例化时忽略新参数(可根据需求改为抛出错误)。
- 移除
MySingleton中多余的super().__init__(*args, **kwargs)调用,避免触发object.__init__的参数错误。 - 明确
MySingleton的构造参数类型,增强代码可读性。
内容的提问来源于stack exchange,提问作者khteh
相关产品推荐
相关产品推荐

