我想在我的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个以上的变量来管理多个函数时,它并不漂亮。
此外,如果您忘记了全局语句,这很容易出错:将创建函数本地的变量,并且不会更改模块的变量,这不是您想要的!
那么,这会被认为是干净的吗?是否有类似于我的方法可以消除“全球”混乱?
或者这根本不是要走的路?
答案 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
的现有唯一实例的引用。
处理整个层次结构
如果Single1
和Single2
类派生自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!"