2012-09-28 45 views
9

堆棧溢出在python中有很多關於全局變量的問題,它似乎對來自其他語言的人產生了一些混亂。範圍規則並不像其他背景的許多人所期望的那樣工作。Python中全局變量的頻率?

與此同時,代碼的組織層次並不是很多,而是在模塊層面。因此,當一切不一定包含在類中時,否則可以在成員變量中找到的狀態可以在模塊級變量中進行。

所以我的問題是2部分:

1)我應該避免使用全局變量(特別是從功能設置中他們並使用全球關鍵字)?

2)如果#1是,是否有共同的模式,他們預計會被使用?

我在一個地方工作的地方很多不同的語言比比皆是,我想緩解混亂,並確保pythonistas不會恨我以後。

謝謝你的任何建設性意見。

+0

還有的'nonlocal'在Python 3.x的:) –

+0

@喬恩克萊門特:那又怎樣? – martineau

回答

7

我強烈建議你閱讀這篇博文,標題爲Singletons and their Problems in Python。它使我重新思考我使用全局變量。一些選擇報價:

但要小心。僅僅因爲你沒有實現單例設計模式,並不意味着你避免了單例的核心問題。單身人士的核心問題是全球共享狀態。一個單身只不過是一個榮耀的全局變量,在像Java這樣的語言中,有很多原因讓你想使用類似單身的東西。在Python中,我們對單例有不同的看法,它有一個非常無辜的名字,它隱藏了血腥的細節:模塊。

這是正確的人:Python模塊是一個單身人士。它和單身模式有相同的問題,只是它有點惡化。

這裏是具有這樣的共享狀態的問題一個例子可能會導致:

爲了不談論不相干的事情,讓我們來看看其中的一個模塊從標準庫中, mimetypes模塊。

看一看:

inited = False 

def init(files=None): 
    global inited 
    db = MimeTypes() 
    ... 

這是附帶的Python,只是更可怕的細節刪除MIME類型模塊的實際代碼。關鍵是,共享狀態。共享狀態是一個布爾標誌,如果該模塊已初始化,則爲True;如果不是,則爲False。現在特定情況可能不是問題(相信我,它是),因爲mimetypes初始化自己,但是你可以看到init函數有一個files參數。如果將文件列表傳遞給該函數,它將使用來自這些文件的MIME信息重新初始化內存中的mime數據庫。現在想象一下,如果你有兩個庫初始化有兩個不同來源的MIME類型......會發生什麼

這是一個普通不過的模式,我已經做到了我自己,而是爲了例如更好的方式做這將是:init返回實現所有方法的類的一個實例,並且代碼的其他部分可以使用init來獲取具有不會影響前者的不同參數的不同實例。 「缺點」是您必須將此實例傳遞給任何不想初始化新實例的代碼,但該「缺點」的優勢在於它明確了您的依賴關係。總之,儘可能地避免它,但是如果你確定代碼有一個隱含的單例,那就去做吧。

+1

使用模塊作爲單例實例的另一個主要限制是與具有'__init __()'方法的類不同,該方法將被調用並可能傳遞參數,因此很難影響/控制在其初始'import'上創建的(單個)實例的構造。 – martineau

+0

@Claudiu謝謝,這個答案給了我超過我的預期:)。 – MrFox

2

全局並不是真的禁忌,它只是你必須記住在你使用它們之前將它們聲明爲全局函數。在我看來,這實際上讓他們更清楚,因爲用戶看到你明確使用全局。

我會更害怕非pythonistas不理解python全局變量,修改你的代碼,而不是添加適當的全局聲明。

3

總之,是的,你應該避免使用global關鍵字。它可能出於某種原因在語言中,但我通常認爲它是一種代碼異味 - 如果您有一些狀態需要保持,請將其封裝在一個類中。這比使用global要脆弱得多。

+1

我認爲永遠不要使用'global'是一條硬而快的規則。將所有狀態保存在類實例中非常簡單,如果您需要創建一個以上的類實例,則應該感謝您的幸運星,您沒有將它們彼此踩在一起。如果這個狀態真的需要共同的話,那麼將它在類層次結構中提升一級。 – Dave

+0

我有時傾向於做'foo = [globalvar]',然後在函數中獲取並設置'foo [0]',所以我不必使用'global'關鍵字。它使得我更明顯的是,我使用全局變量因爲我必須'特別'對待它,但它仍然非常難看,很像使用全局變量 – Claudiu

3

我說了這句話對你的另一個問題,所以這裏是我的2美分:

Python是面向對象的,但你不必使用它。但是,如果你選擇,你可以利用這個類的機制,舉行一些屬性:

class Config(): 
""" 
hold some vars for example. 
""" 
def __init__(self): 
    self.CONFIG_LOG_FILE_DIR='/tmp2/ozn/venus_mon_log/' 
    self.DATE_FORMAT="%Y%m%d" 
    #self.FILE_NAME_BASE_TEMPLATE=eval('datetime.datetime.now().strftime(self.DATE_FORMAT)')+'-venus_utilization.log' 
    self.FILE_NAME_BASE_TEMPLATE='venus_utilization.log' 
    self.FILE_NAME=self.CONFIG_LOG_FILE_DIR+self.FILE_NAME_BASE_TEMPLATE 
    self.MAX_FILE_SIZE=1024*1024*50 # in Byte, 50 in MB. 

你可以創建一個實例,讓您可以訪問屬性:

cfg = Config() 

,然後使用訪問它們:

cfg.MAX_FILE_SIZE 

,甚至改變他們:

cfg.MAX_FILE_SIZE=50000 
    cfg.MAX_FILE_SIZE=calculateNewSize() 

等等... 你也可以做這樣的東西:

# this will print all items that an instance has  
if options.debug: 
    print "DEBUG INFO:" 
    for k,v in vars(cfg).iteritems(): 
     print k,v