我写的python单例 init会调用多次 如何解决?

"init"输出三次

class ConfigLoader(object):
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, *args, **kw):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self, config_file_name="config.json"):
        print("init")
        self.file_path = os.path.join(os.getcwd(), 'config', config_file_name)
        self.json_data = None


c1 = ConfigLoader(config_file_name="config.json")
c2 = ConfigLoader()
c3 = ConfigLoader()
阅读 1.9k
3 个回答

object.__new__

If __new__() is invoked during object construction and it returns an instance of cls, then the new instance’s __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to the object constructor.

构造了三次,__init__ 就会被调用三次。

换一种写法:

import os


class SingleTon(type):
    _instances = {}

    def __call__(self, *args, **kwds):
        if self not in self._instances:
            self._instances[self] = super().__call__(*args, **kwds)
        return self._instances[self]


class ConfigLoader(metaclass=SingleTon):
    def __init__(self, config_file_name="config.json"):
        print("init")
        self.file_path = os.path.join(os.getcwd(), "config", config_file_name)
        self.json_data = None


c1 = ConfigLoader(config_file_name="config.json")
c2 = ConfigLoader()
c3 = ConfigLoader()
print(id(c1), id(c2), id(c3))

这样就只有一次init了。

如果你只是需要单纯的单例上面的就行了(或者看看这个:Python中的单例模式的几种实现方式的及优化,但如果需要不同参数不同实例,可以参考这个:

from frozendict import deepfreeze  # 第三方库,需要安装,用于将字典锁定,用于哈希
from inspect import signature  # 标准库,用于获取函数的签名信息

def parameterized_singleton(cls):
    """
    创建参数化单例装饰器。
    根据传入的参数和关键字参数的不同,为类生成不同的单例实例。
    注意:参数需要可哈希(列表和字典没关系)
    """
    
    sig = signature(cls.__init__)  # 获取类构造函数的签名
    instances = {}  # 用于存储不同参数对应的单例实例
    
    def wrapper(*args, **kwargs):
        """
        根据传入的参数和关键字参数,返回对应的单例实例。

        Args:
            *args: 传给类构造函数的位置参数。
            **kwargs: 传给类构造函数的关键字参数。

        Returns:
            instance (cls): 对应的单例实例。
        """
        bound_args = sig.bind(None, *args, **kwargs)  # 绑定参数
        bound_args.apply_defaults()  # 应用默认参数值
        arg_dict = bound_args.arguments  # 获取所有参数组成的字典
        frozen_args = deepfreeze(arg_dict)  # 将参数字典锁定,用于哈希
        
        if frozen_args in instances:
            return instances[frozen_args]  # 如果实例已存在,则返回它
        
        # 创建实例后,再将其添加到instances中
        instance = cls(*args, **kwargs)
        instances[frozen_args] = instance
        return instance
    
    return wrapper

@parameterized_singleton
class ConfigLoader(object):
    def __init__(self, config_file_name="config.json"):
        print("init")
        # self.file_path = os.path.join(os.getcwd(), 'config', config_file_name)
        # self.json_data = None


c1 = ConfigLoader(config_file_name="config.json")
c2 = ConfigLoader()
c3 = ConfigLoader()
print(c1 is c2)
print(c1 is c3)
# 输出:
# init
# True
# True

上面代码是相对通用的(前提是可哈希)如果你只想校验你代码中的那一个参数,可以简化代码:

def parameterized_singleton(cls):
    
    instances = {}  # 用于存储不同参数对应的单例实例
    
    def wrapper(config_file_name="config.json"):
        
        if config_file_name in instances:
            return instances[config_file_name]  # 如果实例已存在,则返回它
        
        # 创建实例后,再将其添加到instances中
        instance = cls(config_file_name)
        instances[config_file_name] = instance
        return instance
    
    return wrapper


@parameterized_singleton
class ConfigLoader(object):
    def __init__(self, config_file_name="config.json"):
        print("init")
        # self.file_path = os.path.join(os.getcwd(), 'config', config_file_name)
        # self.json_data = None


c1 = ConfigLoader(config_file_name="config.json")
c2 = ConfigLoader()
c3 = ConfigLoader()
print(c1 is c2)
print(c1 is c3)
# 输出:
# init
# True
# True

注意,这段代码在多线程且初始化时有io等长时间的操作时可能出错(因为我没有加锁,要加你自己加)

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏