我之前的回答无效,我已将其删除。但是,我发现了一个高度评价的 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")
因为else
子句中的定义导致SyntaxError
Python 2 中的 a (即使块中的代码永远不会实际执行)。类似于本杰明彼得森的六个模块的with_metaclass()
功能的解决方法,看起来像这样:
class Tracer(Singleton("SingletonBaseClass", (object,), {})):
def __init__(self):
print("Init")
这会动态创建一个继承所需元类的基类,从而避免由于两个 Python 版本之间的元类语法差异而导致的任何错误。(它通过显式使用定义的元类来创建临时基类来实现。)