如何初始化继承自Singleton的对象一次。

6 浏览
0 Comments

如何初始化继承自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()

当您尝试时,您将看到Tracer__init__方法再次被调用。难道拥有一个Singleton的意义不是让另一个实例引用原始实例吗?我不想再次运行__init__方法,因为它可能会覆盖先前的信息。也许单例模式是错的,或者它的使用方式不对?

admin 更改状态以发布 2023年5月24日
0
0 Comments

你的__init__被调用了两次,但作用于同一个对象。你已经创建了单例,但Python并不知道这一点,因此对每个创建的对象都进行初始化。

如果你想追求单例模式,你需要将初始化代码移到__new__中,或移到__new__调用的另一个方法中。

请记住:

  1. 在Java中,单例是常态,但在Python中并不受推荐。

  2. 单例使你的代码更难测试,因为它们是从一个测试到另一个测试传递的全局状态。

0
0 Comments

我的先前回答没有起作用,已经被删除了。然而,我找到了一个高度评价的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版本之间的元类语法差异导致的任何错误。 (它通过显式使用已定义的元类来创建临时基类来实现此目的。)

0