【问题标题】:issue with singleton python call two times __init__单例python调用两次__init__的问题
【发布时间】:2012-08-31 13:43:52
【问题描述】:

我有一个这样的单身

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”被打印了两次,我认为它不应该发生

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

【问题讨论】:

  • 你知道__new__()吗?您似乎正在做一些扭曲以便能够将此模式插入__init__()
  • 非常适合我...你能展示你用来测试它的代码吗?
  • 我试过 new 但我不能,
  • 代码太多,无法分享...
  • 你有没有机会继承这个?

标签: python singleton init


【解决方案1】:

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

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(_singlton = Foo()):
    return _singleton

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

这样做的好处是,如果您需要,您可以继续获得相同的 Foo 实例,但如果您决定在 10 年后不再需要真正的单身人士,那么您将不限于单个实例。

【讨论】:

  • 这里 a 和 b 怎么一样?它不会给你不同的单例实例吗?
  • @codemuncher -- 在哪个版本中?在第一个版本中,您会看到在第一次调用时,它会设置 cls.__instance,然后在后续调用中继续返回相同的 cls.__instance。在第二个版本中,它在创建函数时创建单例,然后继续返回该值(除非您为 _singleton 传递不同的值)。
  • 第二个版本,我下次调用 b = foo_singleton_factory(),是不是要创建另一个对象,我很困惑。
  • 不,默认参数只计算一次——在创建函数时。他们不会为后续调用重新评估。这是python中各种混乱的根源:-)。请参阅stackoverflow.com/questions/1132941/… 了解另一种吸引人的情况。
  • 如果你继承了 Singleton 类,这不起作用
【解决方案2】:

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

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

@singleton
class MyClass:
    ...

(虽然我自己没用过。)

顺便说一句,关于...

我做了一个这样的单身

另外,你应该提到你copied it directly from ActiveState

【讨论】:

  • 我会添加 def getinstance(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs)
【解决方案3】:

由于我们都忽略了您的问题,而是提出了替代的单例实现,所以我会提出我最喜欢的。它利用了一个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__()

那么如何避免这个问题呢?我会引用医生的话:那就别那样做了!


注意:这是在制作下面的 cmets 之后添加的

虽然我在这里,但这里有另一个单例变体,可以最大限度地减少复杂代码的数量。它使用元类:

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 是!将单例属性作为类属性提供给其实例,甚至可能满足您的需求。

【讨论】:

  • 我喜欢这个。我唯一不喜欢的是你真的有一个实例,你根本不能自定义实例的创建。例如你不能做Singleton('foo','bar','baz')(除非你提供__call__,但是你不能将__call__用于其他目的......)
  • 这是个好建议。创建单例的最简单方法是只实例化一次类!
  • 不错,真正的单身人士。奇怪的是,在使用class AnotherClass(Singleton.__class__): passSingletonClass 中定义的函数(“方法”)(如def boo(self): print 'boo!')删除后,它仍然可以被子类化,但它仍然适用于AnotherClass 的实例。沿着这些思路,b = Singleton.__class__() 也有效... isinstance(b, Singleton.__class__)(显然?)提供True
  • ...(续来自b = Singleton.__class__()):b is SingletonFalse
【解决方案4】:

另一种方式:

>>> class Singleton(object):
...     def __new__(cls, *args, **kwargs):
...             try:
...                     return cls._instance
...             except AttributeError:
...                     val = cls._instance = object.__new__(cls, *args, **kwargs)
...                     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 总是调用__new__ 返回的对象的__init__ 方法。

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

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

顺便说一句,如果您想让所有实例都具有相同的状态(而不是身份),那么您可能会对Borg 模式感兴趣。 如果您不确定选择哪一个,请参阅this 答案。

【讨论】:

  • 我对@9​​87654330@ 中的所有魔法以及如何有效地使用它并不十分熟悉,但我的印象是这仍然会在同一个实例上多次调用__init__(这是我认为 OP 试图避免的)
  • 是的@mgilson,你是对的。我经常使用这种方法,因为我不太关心多个 init 调用。我添加了一个简短的解释,说明为什么这个方法会进行多次 init 调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-03
  • 1970-01-01
  • 2011-01-11
  • 2015-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多