为什么python中的Singleton会多次调用__init__,如何避免呢?

Jos*_*eph 3 python singleton initialization

我已经在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
Run Code Online (Sandbox Code Playgroud)

Wor*_*der 8

问题是__new__不返回对象,而是返回一个统一的对象__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")
Run Code Online (Sandbox Code Playgroud)


mar*_*eau 5

另一种简单但完全可行的方法来实现你想要的,不需要超级类或元类,就是让你的类成为一个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()
Run Code Online (Sandbox Code Playgroud)

client.py:

from myclass import MyClass

a = MyClass()
b = MyClass()

print(a, b)
print(a.x, b.x)
Run Code Online (Sandbox Code Playgroud)

输出:

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()
Run Code Online (Sandbox Code Playgroud)

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

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

Derived = Derived()
Run Code Online (Sandbox Code Playgroud)

之后您可以将其添加到“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'
Run Code Online (Sandbox Code Playgroud)