布鲁斯·埃克尔的设计模式代码片段:我对它的工作原理感到困惑
我最近在读布鲁斯·埃克尔的《Thinking in Python》。现在我正在看《模式概念》这一章。在这一章中,埃克尔展示了在Python中实现单例模式的不同方法。但是,我对亚历克斯·马特利的单例代码(使用继承,而不是私有嵌套类)理解得不太清楚。
这是我目前对代码的理解:
- 所有的Singleton对象都是Borg的子类。
- _shared_state最开始是一个空字典。
- _shared_state是一个全局变量;任何使用Borg的对象都会有相同的_shared_state值。
我目前的困惑:
- 这一行的目的是什么:
self.__dict__ = self._shared_state
;或者说这个字典的作用是什么? - 虽然所有的Singletons都是不同的类实例,但它们是如何最终拥有相同的val的?
- 总的来说,我不知道Borg是如何工作的。
非常感谢!
-Tri
*更新:每次创建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 个回答
比较普通类的实例可以帮助你了解它们在做什么:
>>> 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}
这段代码展示了在创建两个对象后,让它们使用相同的字典。Borg/Singleton模式就是让这些实例在创建时使用同一个字典,从而实现共享。
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的意思是“所有实例共享相同的状态”,而单例(singleton)的意思是“只有一个实例”。这两者的效果差不多。Alex Martelli 指出,Borg 是一种 Python 风格的 Monostate,所以可以看看 Monostate 和 Singleton 的对比。
看起来他的单例类更合适地命名为
class ThisClassHasSingletonBehavior(Borg):
....
因为
<__main__.Singleton instance at 0079EF2C>
<__main__.Singleton instance at 0079E10C>
<__main__.Singleton instance at 00798F9C>
证明了 x、y、z 不是同一个实例,尽管它们共享相同的状态。所以它并不是真正的单例,只是有相同的整体效果,而且实现起来很简单。根据我所了解,Borg 是一种优雅的 Python 模式,用于共享状态(相同状态)的行为,而单例则是 C++ 中的优雅模式。虽然这种行为并不一定优雅。
到目前为止,大家的回答都很好,但我也想直接回答一下…… self.__dict__
存储了实例 self
的属性(也就是它的状态)(除非它的类做了一些奇怪的事情,比如定义了 __slots__
;-)。所以,通过确保所有实例都有相同的 __dict__
,我们就能保证它们都有相同的属性,也就是完全相同的状态。因此,Borg 是一种很酷的 Python 实现,属于一种叫做 单态模式,但没有罗伯特·马丁在他关于单态模式的 文章 中提到的那些缺点。
另外,可以看看 这个讨论串,了解单态模式和单例模式的区别——当然,很多讨论和 Python 没什么关系(显然在 Python 中,Borg 并不妨碍继承,恰恰相反!但单例模式也可以通过 __new__
等方式变得相当透明,所以两者的权衡是完全不同的……)。
最有趣的事情是?自从我第一次构思 Borg(在大卫·阿舍尔,现在的 Mozilla Messaging CEO,建议用这个酷名字之前)已经过去了 8 年多,我大概只用过单例或单态模式四次——而这四次中,有三次我很快就重构掉了,换成了更灵活的方法!-)(第四次是一个子系统,结果并不成功,也没得到多少后续的工作/维护;-)。
第二有趣的事情是,Guido 个人非常 厌恶 Borg;-) 其实他对单例模式也没什么好感:他认为在 Python 中,如果需要“单例性”,应该做成一个模块(或者,我想补充一下,做成一个伪装成模块的类实例——可以看看我在《Python 小手册》第 7.2.6 节中的观察,比如 这个盗版链接;-))。不过,Borg 似乎特别让他的设计审美感到不满!-) 这很奇怪,因为他曾基于我关于 Borg 的 《五个简单的例子》 文章提名我为 PSF 会员(这篇文章基本上就是关于 Borg 及其变种和相关考虑的……!-)。唉,看来他可以“厌恶罪恶但爱罪人”,对吧?-)