如何初始化继承自Singleton的对象一次。
如何初始化继承自Singleton的对象一次。
这个问题在这里已经有了答案:
可能重复:
我有以下示例代码,从一个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()
当您尝试时,您将看到Tracer
的__init__
方法再次被调用。难道拥有一个Singleton的意义不是让另一个实例引用原始实例吗?我不想再次运行__init__
方法,因为它可能会覆盖先前的信息。也许单例模式是错的,或者它的使用方式不对?
我的先前回答没有起作用,已经被删除了。然而,我找到了一个高度评价的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
输出:
Init a is b: True
更新
指定元类的语法在Python 2和3之间有所不同。后者需要将Tracer
类定义更改为以下内容:
#!/usr/bin/env python3 class Tracer(object, metaclass=Singleton): def __init__(self): print("Init")
编写一个在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")
因为在Python 2中,else
子句中的定义会导致SyntaxError
(即使实际上永远不会执行块中的代码)。 Benjamin Peterson的six模块的with_metaclass()
函数所做的解决方法类似,看起来像这样:
class Tracer(Singleton("SingletonBaseClass", (object,), {})): def __init__(self): print("Init")
这将动态创建一个基类,该基类继承所需的元类,从而避免了两个Python版本之间的元类语法差异导致的任何错误。 (它通过显式使用已定义的元类来创建临时基类来实现此目的。)