Python: 将模块及其变量视为单例——干净的方式?
我想在我的Python程序中实现一种单例模式。我在考虑不使用类来实现,也就是说,我想把所有与单例相关的函数和变量放在一个模块里,把它当成一个真正的单例。
举个例子,假设这个模块叫做'singleton_module.py':
# singleton_module.py
# Singleton-related variables
foo = 'blah'
bar = 'stuff'
# Functions that process the above variables
def work(some_parameter):
global foo, bar
if some_parameter:
bar = ...
else:
foo = ...
然后,程序的其他部分(也就是其他模块)可以这样使用这个单例:
# another_module.py
import singleton_module
# process the singleton variables,
# which changes them across the entire program
singleton_module.work(...)
# freely access the singleton variables
# (at least for reading)
print singleton_module.foo
我觉得这个主意挺不错的,因为在使用单例的模块里,看起来很简洁。
不过,在单例模块里那些繁琐的'global'声明看起来就不太好。每个处理单例相关变量的函数里都得写这些声明。虽然在这个例子里变量不多,但如果你有10个以上的变量要在多个函数中管理,那就显得很麻烦了。
而且,如果你不小心忘了写'global'声明,就会出错:函数内部会创建局部变量,而模块里的变量不会被改变,这可不是你想要的结果!
所以,这样的做法算不算干净呢?有没有类似的办法可以避免这种'global'的麻烦?
还是说,这根本就不是个好主意?
6 个回答
实现单例模式的一种方法是:在Python中,可以让单例的 __init()__
方法在类的实例已经存在时抛出一个异常。具体来说,类里有一个成员 _single
。如果这个成员不等于 None
,就会抛出异常。
class Singleton:
__single = None
def __init__( self ):
if Singleton.__single:
raise Singleton.__single
Singleton.__single = self
有人可能会认为,用异常来处理单例实例的创建并不是很干净。我们可以用一个方法 handle()
来隐藏实现细节,如下所示:
def Handle( x = Singleton ):
try:
single = x()
except Singleton, s:
single = s
return single
这个 Handle()
方法和C++中单例模式的实现非常相似。在 Singleton
类中,我们可以有这个 handle()
方法。
Singleton& Singleton::Handle() {
if( !psingle ) {
psingle = new Singleton;
}
return *psingle;
}
这个方法可以返回一个新的 Singleton
实例,或者返回已经存在的唯一 Singleton
类的实例。
处理整个层次结构
如果 Single1
和 Single2
类是从 Singleton
类派生出来的,那么通过其中一个派生类就会存在一个 Singleton
的单一实例。可以通过以下方式验证这一点:
>>> child = S2( 'singlething' )
>>> junior = Handle( S1)
>>> junior.name()
'singlething'
也许你可以把所有的变量放在一个全局字典里,这样在你的函数中就可以直接使用这个字典,而不需要用到“global”这个关键词。
# Singleton-related variables
my_globals = {'foo': 'blah', 'bar':'stuff'}
# Functions that process the above variables
def work(some_parameter):
if some_parameter:
my_globals['bar'] = ...
else:
my_globals['foo'] = ...
之所以可以这样做,是因为 Python 的作用域和命名空间。
一个常见的替代方案,用模块作为单例的是亚历克斯·马特利的博格模式:
class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
# and whatever else you want in your class -- that's all!
这个类可以有多个实例,但它们都共享相同的状态。