Python单例/对象实例化

6

我正在学习Python,并且一直在尝试实现一个单例类型的类作为测试。我现有的代码如下:

_Singleton__instance = None

class Singleton:
    def __init__(self):
        global __instance
        if __instance == None:           
            self.name = "The one"
            __instance = self
        else:
            self = __instance

这部分代码有一定效果,但是self = __instance部分似乎出现了问题。我在下面的解释器输出中包含了一些内容以证明这一点(上面的代码保存在singleton.py中):

>>> import singleton
>>> x = singleton.Singleton()
>>> x.name
'The one'
>>> singleton._Singleton__instance.name
'The one'
>>> y = singleton.Singleton()
>>> y.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Singleton instance has no attribute 'name'
>>> type(y)
<type 'instance'>
>>> dir(y)
['__doc__', '__init__', '__module__']

我正在尝试的事情是否可能做到?如果不行,是否有其他方法可以实现?

欢迎任何建议。

谢谢。


1
除了作为一个有趣的学习练习,你有用这个 Singleton 的用例吗?有时候,Singleton 可以比这更简单。Singleton 设计模式——在某种程度上——是 Smalltalk/C++/Java-主义,在 Python 中通常不是必需的。 - S.Lott
5个回答

22

将值赋给函数参数或任何其他局部变量(裸名)不可能对函数外产生任何影响;这同样适用于对(裸名)参数或其他局部变量进行的任何赋值,包括你的self = whatever

相反,应该覆盖__new__

class Singleton(object):

    __instance = None

    def __new__(cls):
        if cls.__instance == None:
            cls.__instance = object.__new__(cls)
            cls.__instance.name = "The one"
        return cls.__instance

我已经进行了其他的增强,比如摒弃了全局变量,旧式类等。

使用Borg(又名单态模式)要比你选择的Highlander(又名单例模式)好得多,但这与你所问的问题不同。


嘿嘿,SO在这里赞助他的(伟大的)创造物 :) 当然我同意。 - drAlberT
谢谢!我想我对__init__方法的理解还不够深入,我被self.name影响对象所迷惑了,但我猜这是Python的一种魔法吧?这个问题更多地是关于理解Python而不是使用单例模式,但我会查看Borg链接的 :) - Jonathan
self.name是一个QUALIFIED名称,而不是BARENAME:对BARENAME进行赋值和对QUALIFIED名称进行赋值可能是完全不同的操作——我在《Python in a Nutshell》的适当章节中花费了相当多的篇幅来讲解这一点,这里重复一遍太多了,所以建议你阅读一下(也许可以从网络上的众多盗版副本中找到:作为作者,我对此并不满意,但确实有大量的盗版!-) - Alex Martelli
在Debian squeeze上使用2.6版本时,尝试实例化此类时会出现“TypeError: type.new(Singleton): Singleton is not a subtype of type”的错误。 - Faheem Mitha
Faheem这位发帖者将他的类命名为“Singleton”。 - Giszmo
1
也许我漏掉了什么,但是每个“__instance”不应该是“cls.__instance”,而“type”应该是“object”吗? - DilithiumMatrix

6

Bruce Eckel的设计模式代码片段:我对它的工作原理感到困惑

class Borg:
  _shared_state = {}
  def __init__(self):
    self.__dict__ = self._shared_state

class MySingleton(Borg):
  def __init__(self, arg):
    Borg.__init__(self)
    self.val = arg
  def __str__(self): return self.val

x = MySingleton('sausage')
print x
y = MySingleton('eggs')
print y
z = MySingleton('spam')
print z
print x
print y
print ´x´
print ´y´
print ´z´
output = '''
sausage
eggs
spam
spam
spam
<__main__. MySingleton instance at 0079EF2C>
<__main__. MySingleton instance at 0079E10C>
<__main__. MySingleton instance at 00798F9C>
'''

查看这个参考:+1。特别是,查看这个答案:https://dev59.com/REjSa4cB1Zd3GeqPJ_MX#1297577 - Daniel Pryden

4

From Singleton Pattern (Python):

class Singleton(type):
    def __init__(self, name, bases, dict):
        super(Singleton, self).__init__(name, bases, dict)
        self.instance = None

    def __call__(self, *args, **kw):
        if self.instance is None:
            self.instance = super(Singleton, self).__call__(*args, **kw)

        return self.instance

class MyClass(object):
    __metaclass__ = Singleton

print MyClass()
print MyClass()

为什么你的单例模式要继承“type”?“Type”是什么?为什么不是“object”? - Alcott
这种实现单例的方式会调用两次__init__,这导致了我很多麻烦,覆盖修改后的变量回到最初的初始化状态。 - madCode

3

这是关于最基本的单例模式。它使用一个类方法来检查单例是否已经创建,如果没有,则创建一个新的单例。还有更高级的方法可以实现,例如覆盖__new__方法

class Singleton:
    instance = None

    @classmethod
    def get(cls):
        if cls.instance is None:
            cls.instance = cls()
        return cls.instance

    def __init__(self):
        self.x = 5       # or whatever you want to do

sing = Singleton.get()
print sing.x  # prints 5

关于为什么你的代码失败,有几个原因。首先,在调用__init__时,一个新对象已经被创建,这违背了单例模式的目的。其次,当你说self = __instance时,这只是重置了本地变量self;这就像是说

def f(x):
  x = 7    # changes the value of our local variable

y = 5
f(y)
print y   # this is still 5

由于 Python 中的变量是按值传递而不是引用,因此您无法说“self = blah”并使其成为您想要的方式的有意义。上述 Singleton 类更符合您的要求,除非您想要高级操作并查看重写 __new__ 运算符。


1
它们是通过共享而不是引用传递的值:http://effbot.org/zone/call-by-object.htm。 - Joschua

0
self = _instance

这段代码不会按照你的期望执行。请阅读有关Python名称处理的相关资料。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接