2011-06-06 43 views
23

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

這似乎是一個不錯的主意,我,因爲它看起來使用的單模塊很乾淨上。

但是,單例模塊中所有這些單調乏味的'全局'語句都很醜陋。它們發生在處理單身相關變量的每個函數中。在這個特定的例子中,這並不是很多,但是當你有10個以上的變量需要管理幾個函數時,這並不好。

此外,如果您碰巧忘記了全局語句,這很容易出錯:函數的局部變量將被創建,並且模塊的變量不會被更改,這不是您想要的!

那麼,這會被認爲是乾淨的?有沒有類似於我的方法來消除'全球'混亂?

或者這是不是要走的路?

+0

爲什麼你首先需要一個單身人士。看起來90%認爲他們想/需要單身人士的人真的沒有。 – delnan 2011-06-06 16:28:06

+0

嗯,當然我不需要*單身。我曾經使用過一個類,它工作得很好,但我想,因爲我只有這個特定類的一個實例,我不能讓它成爲一個單例嗎? – 2011-06-06 16:32:11

+0

好吧,既然你已經找出了單身人士的一個問題,我會說班級勝利;) – delnan 2011-06-06 16:36:50

回答

17

使用一個模塊作爲一個單獨的一個常見的選擇是亞歷克斯·馬爾泰利的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

[亞歷克斯](http://stackoverflow.com/users/95810/alex-martelli)似乎已經從這個消失了 - 他從去年11月以來就沒有見過! 8v( – 2011-06-06 16:54:58

+0

)你是指多個變量指的是同一個實例或多個實例共享相同的狀態?你能說清楚嗎? – Jaydev 2016-09-14 17:20:41

+1

@Jaydev我的意思是我說的 - 多個實例共享相同的狀態。'__shared_state'字典是一個類變量,因此該類的所有實例都共享它們的狀態。 – 2016-09-17 21:11:06

0

與Sven的「Borg模式」建議類似,您可以將所有狀態數據保存在一個類中,而無需創建任何類的實例。我相信這種方法利用了新式的課程。

這種方法甚至可以改編成博格模式,需要提醒的是從類的實例修改狀態成員將需要訪問該實例的__class__屬性(instance.__class__.foo = 'z'而不是instance.foo = 'z',雖然你也可以只做到stateclass.foo = 'z')。

class State: # in some versions of Python, may need to be "class State():" or "class State(object):" 
    __slots__ = [] # prevents additional attributes from being added to instances and same-named attributes from shadowing the class's attributes 
    foo = 'x' 
    bar = 'y' 

    @classmethod 
    def work(cls, spam): 
     print(cls.foo, spam, cls.bar) 

注意修改類的屬性將在連實例化之後的類的實例反映。這包括添加新的屬性和刪除現有的屬性,這些屬性可能會有一些有趣的,可能有用的效果(儘管我也可以看到這在某些情況下可能會導致問題)。自己嘗試一下。

3

也許你可以把所有的變量放在一個全局的字典中,並且你可以在沒有「全局」的情況下直接在你的函數中使用該字典。

# 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

0

一種方法來實現與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模式。我們可以在Singletonhandle()

Singleton& Singleton::Handle() { 

    if(!psingle) { 
    psingle = new Singleton; 
    } 
    return *psingle; 
} 

返回無論是新Singleton實例或Singleton類的現有唯一實例的引用。

處理整個層次

如果Single1Single2類從Singleton通過的派生類存在一個派生的Singleton的單個實例。這可能是與此驗證:

>>> child = S2('singlething') 
>>> junior = Handle(S1) 
>>> junior.name() 
'singlething' 
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中放入任何你想要的東西,但現在它超級乾淨。 :)

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!"