Python:将模块及其变量视为单例 - Clean方法?

时间:2011-06-06 16:25:03

标签: python module singleton

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

这对我来说似乎是个好主意,因为在使用单例的模块中看起来非常干净。

然而,单身模块中所有这些繁琐的“全局”陈述都是丑陋的。它们出现在处理单例相关变量的每个函数中。在这个特定的例子中,这并不多,但是当你有10个以上的变量来管理多个函数时,它并不漂亮。

此外,如果您忘记了全局语句,这很容易出错:将创建函数本地的变量,并且不会更改模块的变量,这不是您想要的!

那么,这会被认为是干净的吗?是否有类似于我的方法可以消除“全球”混乱?

或者这根本不是要走的路?

6 个答案:

答案 0 :(得分:22)

将模块用作单身的常见替代方法是Alex Martelli的Borg pattern

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

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

答案 1 :(得分:4)

也许您可以将所有变量放在全局字典中,并且可以在没有“全局”的情况下直接在函数中使用dict。

# 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 Scopes and Namespaces

答案 2 :(得分:1)

使用Python实现单例模式的一种方法也可以是:

如果类的实例已经存在,则使用singleton __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()方法非常类似于Singleton模式的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'

答案 3 :(得分:0)

与Sven的“Borg模式”建议类似,您可以将所有状态数据保存在一个类中,而无需创建该类的任何实例。我相信这种方法利用了新式的课程。

此方法甚至可以适用于Borg模式,但需要注意的是,从类实例修改状态成员需要访问实例的__class__属性而不是instance.__class__.foo = 'z' {1}},尽管您也可以instance.foo = 'z')。

stateclass.foo = 'z'

请注意,即使在实例化之后,对类的实例的修改也会反映在类的实例中。这包括添加新属性和删除现有属性,这可能有一些有趣的,可能有用的效果(虽然我也可以看到在某些情况下这可能会导致问题)。亲自尝试一下。

答案 4 :(得分:0)

建立WillYang's answer并进一步提高清洁度:定义一个简单的类来保存全局字典,以便更容易引用:

class struct(dict):
    def __init__(self, **kwargs):
        dict.__init__(self, kwargs)
        self.__dict__ = self

g = struct(var1=None, var2=None)

def func():
    g.var1 = dict()
    g.var3 = 10
    g["var4"] = [1, 2]
    print(g["var3"])
    print(g.var4)

就像你在g中放置任何你想要的东西之前一样,但现在它非常干净。 :)

答案 5 :(得分:0)

对于合法的单身人士:

class SingletonMeta(type):
    __classes = {} # protect against defining class with the same name
    def __new__(cls, cls_name, cls_ancestors, cls_dict): 
         if cls_name in cls.__classes:
             return cls.__classes[cls_name]
         type_instance = super(SingletonMeta, cls).__new__(cls, cls_name, cls_ancestors, cls_dict) # pass 'type' instead of 'cls' if you dont want SingletonMeta's attributes reflected in the class
         return type_instance() # call __init__

class Singleton:
    __metaclass__ = SingletonMeta
    # define __init__ however you want

    __call__(self, *args, *kwargs):
        print 'hi!'

要确定它确实是一个单例,请尝试实例化此类或从中继承的任何类。

singleton = Singleton() # prints "hi!"