2012-08-22 134 views
6

我有幾個在我的Python應用程序中普遍存在並且應該只有一個全局實例(例如Logger,DbConnection)的類。 Python不支持類中的靜態變量/方法,因此在這裏創建單例的常用Java/C++方式不起作用。我已經尋找了在Python中實現單例的替代方案。我想要一個簡單的(如果可能的話,不要編程)和乾淨的實現。這看起來很不錯:Python中的全局變量和單例

class MyClass(object): 
    def a(): 
     pass 

singleton = MyClass() 

使用Singleton是簡單

import myClass 
myClass.singleton.a() 

直接分配可以由創建功能所取代,如果對象的初始化並非如此簡單。

我也可以在模塊範圍內創建一個getInstance(),並始終使用它來獲取myObj。

問題1)這工作正常嗎?模塊代碼(myObj賦值)僅在第一次將其導入其他模塊時運行,並且每次將該模塊導入某處時都不會創建myObj?

我看到的另一種方法是使用全局模塊。喜歡的東西:

from myClass1 import MyClass1 
from myClass2 import MyClass2 

myObj1 = MyClass1() 
myObj2 = MyClass2() 

使用此:

import globals 
globals.myObj1.a() 

我傾向於選擇第一種選擇。

問題2)在兩種解決方案之間,你有什麼建議?

問題3)第三種解決方案是將諸如Logger等廣泛的對象傳遞給幾個類/函數,但這不是一個好的解決方案。有沒有更好的解決方案在這裏沒有提到?

我知道使用全局變量和單例的缺點。但是,擁有一個全球性的國家在我的申請中並不是一個大問題。我更喜歡代碼清晰且易於使用的解決方案。

+0

你問題的前提是錯誤的。首先,python支持靜態方法和變量。其次,爲什麼不使用現有的標準庫進行日誌記錄和數據庫訪問? –

+0

Python直接支持靜態方法/變量(沒有註釋破解)?你的第二個問題偏離了這個問題(單例實現),用任何一個單例類代替logging/db訪問類,並且它在應用程序中被廣泛使用。 –

回答

6

如果你想有一個只有一個實例的logger類,可以將它作爲一個獨立的模塊。

# In logging.py 
def log(msg): 
    print msg 

然後從任何腳本你想登錄。

from logging import log 
log("A critical error occured.") 
+1

我的記錄器類中有屬性(例如日誌文件名)。如果我在模塊函數中轉換我的類方法,那麼以前的類屬性將成爲模塊變量。這不是問題嗎? (例如,用戶使用「從日誌導入*」(錯誤我知道,但不過),我的變量會混亂應用程序變量空間 –

+0

@ user1550682:模塊實際上只是Python中的單例,它是一個對象,只有一個實例,你可以像任何其他值一樣傳遞它,所以沒問題 –

+5

@ user1550682 - 用一個下劃線作爲你的私有變量的前綴,它們不會用'import *語句,例如'_filename =「bla.bla」' – Aesthete

-1

如何製作單身人士的正確答案?別。你應該明確地傳遞一切需要使用它的引用。您可以減少工廠功能,包裝類等的工作量。

即使有一些東西,如本質上是全局性的屏幕或記錄器,您仍然應該明確地通過它以允許進行單元測試。請注意,這僅適用於旨在成爲最終設計一部分的事物。如果您的記錄器只是一個快速入門的調試工具,請隨時將其設置爲全局。

+0

正如我原來的文章中提到的,我意識到使用單例的缺點。自從我第一次聽說他們到今天,他們的使用是有爭議的。將一個記錄器變量傳遞給應用程序中的所有類和函數(如果它們不在類中)似乎是烏托邦式的,我從來沒有見過一個真正的應用程序。 –

+0

我已經做了幾次,雖然承認相對簡單的腳本。而且我計劃在我目前的項目中做得更完整。但你說得對,這有點烏托邦。 – Antimony

+1

Alan:許多應用程序使用全局記錄器實例,或者將其傳遞到應用程序其他部分導入的設置模塊中。此外,許多應用程序還依賴於應用程序或設置模塊,該模塊存儲全局變量,單例實例或要在整個應用程序中使用的線程。 Django和Celery都是這樣做的應用程序的例子。通過使用代理和狀態設計模式,它簡化了依賴單例的需要,儘管最終這是發生的事情,只是以不同的形式。 –

0

考慮創建如下:

  1. State類
  2. 代理類

狀態類將創建在INIT初始化一個線程。 py模塊。狀態類將具有將類實例傳遞到線程並將弱參數保存到其中的方法。

稍後,可以使用代理類訪問線程中的類實例。如果未創建類對象,則代理將創建所需類的實例並將其保存到線程中。

使用此模式,可以將每個類的實例數限制爲1,並確保無論何時應用程序的一部分正在運行,都不必重新創建需要全局重複使用的任何實例。