为什么 python 中的 Singleton 多次调用 __init__ 以及如何避免?

Why Singleton in python calls __init__ multiple times and how to avoid it?

我在 python 中实现了单例模式,但我注意到 init 被无用地调用,每次我调用 MyClass,尽管返回了相同的实例。

我怎样才能避免它?

class Test(object):
    def __init__(self, *args, **kwargs):
        object.__init__(self, *args, **kwargs)

class Singleton(object):
  _instance = None

  def __new__(cls):
    if not isinstance(cls._instance, cls):
        cls._instance = object.__new__(cls)
    return cls._instance

class MyClass(Singleton):
    def __init__(self):
        print("should be printed only 1 time")
        self.x=Test()
        pass

a = MyClass() # prints: "should be printed only 1 time"
b = MyClass() # prints ( again ): "should be printed only 1 time"

print(a,b) # prints: 0x7ffca6ccbcf8 0x7ffca6ccbcf8
print(a.x,b.x) # prints: 0x7ffca6ccba90 0x7ffca6ccba90

__init__,如果实现,总是调用__new__返回的任何内容;通常,您应该实施其中之一,而不是两者。相反,您可以在 MyClass 上实现 __new__,并且仅当 x 属性尚不存在时才初始化它:

class MyClass(Singleton):

    def __new__(cls):
        inst = super(MyClass, cls).__new__(cls)
        if not hasattr(inst, 'x'):
            print("should be printed only 1 time")
            inst.x = Test()
        return inst

正在使用:

>>> a = MyClass()
should be printed only 1 time
>>> b = MyClass()
>>> a is b
True
>>> a.x is b.x
True

问题是 __new__ 不是 return 对象,它 return 是一个单元化对象,之后会调用 __init__

你根本无法避免。您可以执行以下操作(使用元类型):

class Singleton(type):
    def __init__(self, name, bases, mmbs):
        super(Singleton, self).__init__(name, bases, mmbs)
        self._instance = super(Singleton, self).__call__()

    def __call__(self, *args, **kw):
        return self._instance

class Test(metaclass = Singleton):
    # __metaclass__ = Singleton # in python 2.7
    def __init__(self, *args, **kw):
        print("Only ever called once")

另一个简单但完全可行的方法来实现你想要的东西,它不需要超级或元 classes,就是让你的 class 成为一个 Python 模块,本质上是单例对象。

我的意思是:

myclass.py:

class Test(object):
    pass

class MyClass(object):
    def __init__(self):
        print("in MyClass.__init__, should be printed only 1 time")
        self.x = Test()

    def __call__(self, *args, **kwargs):
        classname = type(self).__name__
        return globals()[classname]

MyClass = MyClass()

client.py:

from myclass import MyClass

a = MyClass()
b = MyClass()

print(a, b)
print(a.x, b.x)

输出:

in MyClass.__init__, should be printed only 1 time
<myclass.MyClass object at 0x02200B30> <myclass.MyClass object at 0x02200B30>
<myclass.Test object at 0x02200C10> <myclass.Test object at 0x02200C10>

可以从 MyClass 派生一个子class,但您必须这样做:

class Derived(type(MyClass)):
    def __init__(self):
        print("in Derived.__init__, should be printed only 1 time")

Derived = Derived()

之后您可以将其添加到 'client.py':

from myclass import Derived

a = Derived()
b = Derived()

print(a,b)
print(a.x,b.x)  # AttributeError: 'Derived' object has no attribute 'x'

另一种创建单例对象的方法是让另一个 class 方法说 get_instance 这将确保您的 class 有一个单例对象..这是代码片段:

class Singleton(object):
    __instance = None

    @classmethod
    def get_instance(cls):
        if not cls.__instance:
            Singleton()
        return cls.__instance

    def __init__(self):
        if Singleton.__instance is None:
            Singleton.__instance = self
            print("Object initialized")

singleton_1 = Singleton.get_instance()
singleton_2 = Singleton.get_instance()

if singleton_1 is singleton_2:
    print("Same")
else:
    print("Different")

以上代码片段产生以下输出:

Object initialized
Same