来自Design Pattern的Bruce Eckel的代码片段:我对它是如何工作感到困惑

时间:2009-08-18 20:28:47

标签: python

我一直在阅读布鲁斯·埃克尔的Thinking in python。目前,我正在阅读模式概念章节。在本章中,Eckel展示了python中Singletons的不同实现。但是我对Alex Martelli的Singleton代码(利用继承,而不是私有的嵌套类)有一个不清楚的理解。

这是我对目前代码的理解:

  • 所有 Singleton 对象都是 Borg 的子类
  • _shared_state 最初是一个空字典
  • _shared_state 是一个全局变量;使用 Borg 的任何对象都具有相同的 _shared_state

到目前为止我的困惑:

  • 这一行的目的是什么:self.__dict__ = self._shared_state;或者词典的目的
  • Singletons 的所有对象如何最终具有相同的 val ,即使它们都是该类的不同实例。
  • 总体而言,我不知道 Borg 是如何运​​作的

许多人提前感谢!

- 三

*更新:每次 Singleton 对象创建后, _shared_state 中存储了什么?

#: Alex' Martelli's Singleton in Python
class Borg:
  _shared_state = {}
  def __init__(self):
    self.__dict__ = self._shared_state

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

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

4 个答案:

答案 0 :(得分:10)

到目前为止答案很好,但我也要直接回答...... self.__dict__保存实例self的属性(即状态)(除非它的类具有特殊的东西,如定义{{} 1}} ;-)。因此,通过确保所有实例具有相同的__slots__,我们确保它们都具有相同的属性,即完全相同的状态。因此,Borg是一般Monostate模式的巧妙Python实现,但没有Robert Martin在他的essay中在Monostate 中用C ++ 确定的任何缺点。

另请参阅Monostate与Singleton的this SO thread - 当然,大多数讨论都与Python无关(显然在Python中,Borg并不妨碍继承,相反!)但是Singleton可以由于例如__dict__而非常透明,所以权衡是完全不同的......)。

最有趣的事情是什么?自从我第一次构思Borg以来的8年多(在现任Mozilla Messaging首席执行官大卫·阿舍尔之前,提出了酷博格的名字)我有机会使用任何类型的单身人士或单身人士,可能共四次 - 以及三次在这四个中我很快就重新构建了它以支持更灵活的方法! - )(第四次是一个子系统,它没有证明非常成功,也没有得到太多的后续工作/维护; - )。

第二个最有趣的事情是,Guido,个人而言,讨厌 Borg ;-)。并不是说他对Singleton有任何真正的喜欢:他认为在Python中“单一性”,如果需要,应该作为一个模块完成(或者,我想补充一下,伪装成模块的类实例 - 参见例如我的在果壳的第7.2.6节中观察,例如在this海盗副本中;-)。但是,博格似乎特别冒犯了他的设计美学! - )奇怪,因为他确实根据我的Five Easy Pieces文章提名我的PSF会员资格,这基本上都是关于博格(及其变体和考虑因素)的所有内容。 ! - )。好吧,猜猜他可以“讨厌罪恶但爱罪人”,嗯? - )

答案 1 :(得分:6)

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

1)因为Borg._shared_state在类级别(而不是__init__)初始化,所以它实际上是静态的,例如由所有类实例共享。

2)self.__dict__是所有对象都有的字典;它包含所有实例属性。因此,

self.a=1
assert(self.a == self.__dict__['a']) #True

3)请注意,Borg表示“所有实例共享相同的状态”,单例表示“只有一个实例”。这几乎是同样的效果。 Alex Martelli指出Borg是一个pythonic Monostate,所以请看Monostate vs Singleton on SO

似乎他的Singleton类更恰当地命名为

class ThisClassHasSingletonBehavior(Borg):
    ....

,因为

<__main__.Singleton instance at 0079EF2C>
<__main__.Singleton instance at 0079E10C>
<__main__.Singleton instance at 00798F9C>

证明x,y,z不是同一个实例,即使它们共享相同的状态。所以它不是单身,它只是具有相同的整体效果,而且很容易。 AFAICT Borg是一个优雅的python模式,用于共享状态(相同状态)行为,其中Singleton是一个优雅的cpp模式。并不是说这种行为是优雅的。

答案 2 :(得分:2)

与普通班级的实例相比,可以帮助您了解他们正在做的事情:

>>> class NormalClass:
       def __init__(self, arg):
           self.x = arg


>>> a = NormalClass(1)
>>> b = NormalClass(2)
>>> a.x
1
>>> a.__dict__
{'x': 1}
>>> b.x
2
>>> b.__dict__
{'x': 2}

Note how the instances here each have their own unshared __dict__ where
their own x attribute is stored.

编辑添加:现在,让我们让这些普通对象共享一个字典,看看会发生什么......

>>> Another_Dictionary_We_Make = {}
>>> Another_Dictionary_We_Make['x'] = 1000
>>> Another_Dictionary_We_Make
{'x': 1000}
>>> a.__dict__ = Another_Dictionary_We_Make
>>> a.x
1000
>>> b.x
2
>>> b.__dict__ = Another_Dictionary_We_Make
>>> b.x
1000
>>> b.x = 777
>>> b.__dict__
{'x': 777}
>>> a.x
777
>>> a.__dict__
{'x': 777}

这表明在创建两个对象后, dict 是相同的。 Borg / Singleton只是通过初始化实例来共享相同的字典,以便在创建它们时使用相同的 dict

答案 3 :(得分:0)

我认为问题是:Borg._shared_state如何更新?答案:在每个实例中都通过self.__dict__。 通过self.__dict__ = self._shared_state,您可以访问每个实例的Borg._shared_stateself.__dict__。当您更改实例属性时,您更改了该实例的__dict__,并且由于该实例的__dict__指向Borg._shared_state,您实际上也会更改Borg._shared_state。 在python中,当您说var1 = var2时,var1指向var2的值,并且它可以访问该值。这就是为什么var1 = a_different_value实际上会更改var2的值以及var1的值的原因。 我也正在阅读上述书籍(通过BitBucket在线)。比特巴克说,几年前开发停止了。我不知道这是为什么。也许作者可以对此发表评论。感谢Alex Martelli这本伟大的书。