如何初始化Singleton派生对象一次

Ale*_*lex 2 python singleton

可能重复:
在Python中定义单例是否有简单,优雅的方法?

我有以下示例代码,其中我从Singleton派生一个类(希望它是一个):

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance

class Tracer(Singleton):         
    def __init__(self):
        print "Init"

a = Tracer()
b = Tracer()
Run Code Online (Sandbox Code Playgroud)

当你尝试它时,你会看到再次调用__init__方法Tracer.是不是有单身人士让另一个实例引用原始实例?我不想__init__再次运行该方法,因为它可能会覆盖以前的信息.也许单身人士是错的还是有用的?

mar*_*eau 6

我之前的回答没有用,我删除了它.但是我发现了一个评价很高的SO 答案.主要区别在于它使用Singleton 元类而不是基类,并重载__call__()其实例类的__new__()方法而不是其方法.这为其单例类实例的实例的创建过程提供了所需的控制.可以定义一个删除其中一个或多个的附加方法 - 比如用于测试目的.

另一个值得注意的实现细节是,元类维护一个字典_instances而不是只能容纳单个值的字典.这允许它跟踪无限数量的单例实例(因为它可能是多个元类,因为它可以重用).

将它应用于您的示例代码将执行以下操作:

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
Run Code Online (Sandbox Code Playgroud)

输出:

Init
a is b: True
Run Code Online (Sandbox Code Playgroud)

更新

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

#!/usr/bin/env python3

class Tracer(object, metaclass=Singleton):
    def __init__(self):
        print("Init")
Run Code Online (Sandbox Code Playgroud)

写一个东西,将在工作两个 Python版本2和3是可能的,但稍微有些复杂,因为你不能简单地定义有条件像这样:

## 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")
Run Code Online (Sandbox Code Playgroud)

因为else子句中的定义导致SyntaxErrorPython 2中的一个(即使块中的代码永远不会被执行).类似于Benjamin Peterson的六个模块的with_metaclass()功能的解决方法,看起来像这样:

class Tracer(Singleton("SingletonBaseClass", (object,), {})):
    def __init__(self):
        print("Init")
Run Code Online (Sandbox Code Playgroud)

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