Python: 将模块及其变量视为单例——干净的方式?

39 投票
6 回答
26120 浏览
提问于 2025-04-16 19:02

我想在我的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 个回答

1

实现单例模式的一种方法是:在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 类的实例。

处理整个层次结构

如果 Single1Single2 类是从 Singleton 类派生出来的,那么通过其中一个派生类就会存在一个 Singleton 的单一实例。可以通过以下方式验证这一点:

>>> child = S2( 'singlething' )
>>> junior = Handle( S1)
>>> junior.name()
'singlething'
7

也许你可以把所有的变量放在一个全局字典里,这样在你的函数中就可以直接使用这个字典,而不需要用到“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 的作用域和命名空间

29

一个常见的替代方案,用模块作为单例的是亚历克斯·马特利的博格模式

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state
    # and whatever else you want in your class -- that's all!

这个类可以有多个实例,但它们都共享相同的状态。

撰写回答