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
调用的另一个方法中
请记住:
单例
元类而不是基类,并重载其实例类的\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:再次感谢。我确实花了一些时间查看了大量的线索和冗长的答案,并选择了这一个…还有许多其他人在链接的答案中做了选择(这是一些其他有用的信息)。对不起,把你/我们的时间浪费在我的另一个假答案上。