Python 如何一次性初始化单例派生对象

Python 如何一次性初始化单例派生对象,python,singleton,Python,Singleton,可能重复: 我有以下示例代码,其中我从一个单例派生了一个类(希望是这样的): 当您尝试它时,您将看到再次调用Tracer的\uuuu init\uuu方法。让一个单身汉让另一个实例引用原来的实例,这不是很有意义吗?我不想再次运行\uuu init\uu方法,因为它可能会覆盖以前的信息。可能是singleton错误了,或者它正在使用?您的\uuuu init\uuuu被调用两次,但在同一个对象上。您已经创建了一个单例,但Python不知道它是单例,所以它会初始化创建的每个对象 如果您想采用单例模

可能重复:

我有以下示例代码,其中我从一个单例派生了一个类(希望是这样的):


当您尝试它时,您将看到再次调用
Tracer
\uuuu init\uuu
方法。让一个单身汉让另一个实例引用原来的实例,这不是很有意义吗?我不想再次运行
\uuu init\uu
方法,因为它可能会覆盖以前的信息。可能是singleton错误了,或者它正在使用?

您的
\uuuu init\uuuu
被调用两次,但在同一个对象上。您已经创建了一个单例,但Python不知道它是单例,所以它会初始化创建的每个对象

如果您想采用单例模式,则必须将初始化代码移动到
\uuuuu new\uuuuu
中,或移动到
\uuuu new\uuuu
调用的另一个方法中

请记住:

  • 单例是Java中的标准,但在Python中是不受欢迎的

  • 单例使您的代码更难测试,因为它们是从一个测试到下一个测试的全局状态

  • 我以前的回答不起作用,我已经删除了它。然而,我发现了一个很高的评级,确实如此。主要区别在于,它使用
    单例
    元类而不是基类,并重载其实例类的
    \uuuuuuuuu call()
    方法,而不是它们的
    \uuuu new\uuuuuuuuuuuu()
    方法。这使它能够控制其单例类实例的实例创建过程。可以定义一个额外的方法来删除其中一个或多个,比如出于测试目的

    另一个值得注意的实现细节是元类维护一个
    \u实例的字典
    ,而不是只能保存一个值的字典。这允许它跟踪无限多个单例实例(因为它可能是多个元类的元类,因为它是可重用的)

    将其应用于示例代码将执行以下操作:

    class Singleton(type):
        """Metaclass."""
        _instances = {}
    
        def __call__(cls, *args, **kwargs):
            if cls not in cls._instances:
                cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
            return cls._instances[cls]
    
    class Tracer(object):
        __metaclass__ = Singleton
    
        def __init__(self):
            print("Init")
    
    a = Tracer()
    b = Tracer()
    print('a is b: {}'.format(a is b))  # same object? -> True
    
    ## Won't work ##
    
    if sys.version_info[0] < 3:  # Python 2?
        class Tracer(object):
            __metaclass__ = Singleton
    
            def __init__(self):
                print("Init")
    
    else:  # Python 3
        class Tracer(object, metaclass=Singleton):  # causes SyntaxError in Python 2
    
            def __init__(self):
                print("Init")
    
    class Tracer(Singleton("SingletonBaseClass", (object,), {})):
        def __init__(self):
            print("Init")
    
    输出:

    Init
    a是b:对
    
    更新

    指定元类的语法在Python2和Python3之间有所不同。对于后者,您需要将
    Tracer
    类定义更改为:

    #!/usr/bin/env python3
    
    class Tracer(object, metaclass=Singleton):
        def __init__(self):
            print("Init")
    
    编写一个在Python版本2和版本3中都能工作的东西是可能的,但要复杂一些,因为不能像这样有条件地定义它:

    class Singleton(type):
        """Metaclass."""
        _instances = {}
    
        def __call__(cls, *args, **kwargs):
            if cls not in cls._instances:
                cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
            return cls._instances[cls]
    
    class Tracer(object):
        __metaclass__ = Singleton
    
        def __init__(self):
            print("Init")
    
    a = Tracer()
    b = Tracer()
    print('a is b: {}'.format(a is b))  # same object? -> True
    
    ## Won't work ##
    
    if sys.version_info[0] < 3:  # Python 2?
        class Tracer(object):
            __metaclass__ = Singleton
    
            def __init__(self):
                print("Init")
    
    else:  # Python 3
        class Tracer(object, metaclass=Singleton):  # causes SyntaxError in Python 2
    
            def __init__(self):
                print("Init")
    
    class Tracer(Singleton("SingletonBaseClass", (object,), {})):
        def __init__(self):
            print("Init")
    

    这将动态创建一个基类,该基类继承所需的元类,从而避免由于两个Python版本之间的元类语法差异而导致的任何错误。(它通过显式使用定义的元类创建临时基类来实现。)

    为什么需要单例?只需要有一个模块级全局,您可以为其分配一个实例,并用下划线命名该类,这样人们就知道不要实例化它。我不想依赖人们“了解”任何人的约定。@Alex任何试图使用您的代码的人都不会假定它是单例代码,当他们尝试创建多个实例时,会导致混乱。不管是哪种方式,你都需要解释,我认为单身是不太清楚的,而且需要更多的工作。当然,你可能根本不需要上课。模块级常量可能就是您想要的。@eyquem:看看我的这个备选答案。您找到了!我没有时间研究我们在其他线程中找到的所有其他解决方案,然后将您的解决方案与其他解决方案进行比较。但它是有效的,我认为使用元类的能力值得一次投票。@eyquem:再次感谢。我确实花了一些时间查看了大量的线索和冗长的答案,并选择了这一个…还有许多其他人在链接的答案中做了选择(这是一些其他有用的信息)。对不起,把你/我们的时间浪费在我的另一个假答案上。