单例 python 调用两次 __init__ 的问题

问题描述 投票:0回答:4

我有一个这样的单身人士

class Singleton:

    class __impl:
        def __init__(self):
            print "INIT"

    __instance = None

    def __init__(self):
        # Check whether we already have an instance
        if Singleton.__instance is None:
            Singleton.__instance = Singleton.__impl()

        # Store instance reference as the only member in the handle
        self.__dict__['_Singleton__instance'] = Singleton.__instance

    def __getattr__(self, attr):
        """ Delegate access to implementation """
        return getattr(self.__instance, attr)

    def __setattr__(self, attr, value):
        """ Delegate access to implementation """
        return setattr(self.__instance, attr, value)

当我创建了几个 Singleton 实例时,我收到了两次对 init 的调用,我的意思是“INIT”被打印了两次,我认为这不应该发生

有人知道这有什么问题或者有更好的方法来实现这个??

python singleton init
4个回答
27
投票

这里有一个稍微简单一点的编写单例的方法:

class Singleton(object):
    __instance = None
    def __new__(cls):
        if cls.__instance is None:
            cls.__instance = super(Singleton,cls).__new__(cls)
            cls.__instance.__initialized = False
        return cls.__instance

    def __init__(self):      
        if(self.__initialized): return
        self.__initialized = True
        print ("INIT")

a = Singleton()
b = Singleton()
print (a is b)

虽然可能有更好的方法。我不得不承认,我从来不喜欢单身人士。我更喜欢工厂类型的方法:

class Foo(object):
    pass

def foo_singleton_factory(_singleton= Foo()):
    return _singleton

a = foo_singleton_factory()
b = foo_singleton_factory()
print (a is b)

这样做的好处是,如果您愿意,您可以继续获得相同的 Foo 实例,但如果您决定 10 年后您不想要真正的单例,则不受限于单个实例。


10
投票

PEP 318 有一个类的单例装饰器示例:

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...

(虽然我自己没用过。)

顺便说一句,关于...

我做了一个这样的单例

此外,您应该提到您直接从 ActiveState 复制它。


3
投票
由于我们都忽略了您的问题并提出了替代的单例实现,因此我将介绍我最喜欢的实现。它利用了这样一个事实:无论导入多少次,Python 模块都只会加载一次。

它也基于Python的座右铭“

我们都是同意的成年人”,因为,如果你真的想要,你可以多次实例化它......但是你真的必须付出额外的努力做错了。

所以在

mysingleton.py

class SingletonClass(object): def __init__(self): # There's absolutely nothing special about this class # Nothing to see here, move along pass # Defying PEP8 by capitalizing name # This is to point out that this instance is a Singleton Singleton = SingletonClass() # Make it a little bit harder to use this module the wrong way del SingletonClass

然后像这样使用它:

from mysingleton import Singleton # Use it!

我说你必须付出额外的努力才能做错事。以下是如何创建单例类的两个实例,使其不再是单例:

another_instance = Singleton.__class__()

那么如何避免这个问题呢?我引用医生的话:

那就不要这样做!


注意:这是在发表以下评论后添加的

当我这样做时,这是另一个单例变体,可以最大限度地减少复杂代码的数量。它使用元类:

class SingletonMeta(type): # All singleton methods go in the metaclass def a_method(cls): return cls.attribute # Special methods work too! def __contains__(cls, item): return item in cls.a_list class Singleton(object): __metaclass__ = SingletonMeta attribute = "All attributes are class attributes" # Just put initialization code directly into the class a_list = [] for i in range(0, 100, 3): a_list.append(i) print Singleton.a_method() print 3 in Singleton

在 python 3 中,您可以像这样创建单例实例:

class Singleton(metaclass=SingletonMeta): attribute = "One... two... five!"

现在这个有点不确定,因为单例是一个

,并且您可以创建单例实例。理论上这是可以的,因为即使它有实例,单例仍然是单例,但你需要记住 Singleton()

 不是单例 - 
Singleton
 是!它甚至可能满足您的需要,让单例属性可以作为类属性供其实例使用。


1
投票
另一种方式:

class Singleton(object): def __new__(cls, *args, **kwargs): try: return cls._instance except AttributeError: val = cls._instance = object.__new__(cls) return val
用作:

>>> class A(Singleton): pass ... >>> a = A() >>> a2 = A() >>> a2 is a True >>> class B(Singleton): pass ... >>> b = B() >>> b2 = B() >>> b2 is b True >>> b is a False >>> class D(Singleton): ... def __init__(self, v): self.v = v ... >>> d = D(1) >>> d.v 1
如果您担心多次调用

__init__

,那么可以选择使用装饰器或元类。

重写

__new__

 方法允许多个 
__init__
 调用,因为如果返回的值是该类的实例,Python 总是调用 
__init__
 返回的对象的 
__new__
 方法。

无论如何,我认为使用装饰器是最好的事情,因为它可能是更简单的解决方案。

如果您想了解在 python 中创建单例的更多方法,请阅读

this问题。

顺便说一句,如果您想让所有实例具有相同的状态(而不是身份),那么您可能会对

Borg 模式感兴趣。 如果您不确定选择哪一个,请参阅this答案。

© www.soinside.com 2019 - 2024. All rights reserved.