【Python】 Python:locals 和 globals2011-09-09 14:27 64人閱讀 評論(0) 收藏 舉報
靜夜思 » 日誌 » Python:locals 和 globals
熱1發表於2010-03-21 23:32 已有678人讀過 標籤: Python globals locals
Python有兩個內建的函數,locals 和globals,它們提供了基於字典的訪問局部和全域變數的方式。
首先,是關於名字空間的一個名詞解釋。是枯燥,但是很重要,所以要耐心些。Python使用叫做名字空間的東西來記錄變數的軌跡。名字空間只是一個字典,它的鍵字就是變數名,字典的值就是那些變數的值。實際上,名字空間可以象Python的字典一樣進行訪問,一會我們就會看到。
在一個Python程式中的任何一個地方,都存在幾個可用的名字空間。每個函數都有著自已的名字空間,叫做局部名字空間,它記錄了函數的變數,包括函數的參數和局部定義的變數。每個模組擁有它自已的名字空間,叫做全域名字空間,它記錄了模組的變數,包括函數、類、其它匯入的模組、模組層級的變數和常量。還有就是內建名字空間,任何模組均可訪問它,它存放著內建的函數和異常。
當一行代碼要使用變數 x 的值時,Python會到所有可用的名字空間去尋找變數,按照如下順序:
- 局部名字空間 - 特指當前函數或類的方法。如果函數定義了一個局部變數 x,Python將使用這個變數,然後停止搜尋。
- 全域名字空間 - 特指當前的模組。如果模組定義了一個名為 x 的變數,函數或類,Python將使用這個變數然後停止搜尋。
- 內建名字空間 - 對每個模組都是全域的。作為最後的嘗試,Python將假設 x 是內建函數或變數。
如果Python在這些名字空間找不到 x,它將放棄尋找並引發一個 NameError 的異常,同時傳 遞 There is no variable named 'x' 這樣一條資訊,回到第一章,你會看到一路上都有這樣的資訊。但是你並沒有體會到Python在給出這樣的錯誤之前做了多少的努力。
|
Python 2.2將引入一種略有不同但重要的改變,它會影響名字空間的搜尋順序:嵌套的範圍。在Python 2.0中,當你在一個嵌套函數或 lambda 函數中引用一個變數時,Python會在當前(嵌套的或 lambda)函數的名字空間中搜尋,然後在模組的名字空間。Python 2.2將支在當前(嵌套的或 lambda)函數的名字空間中搜尋,然後是在父函數的名字空間,接著是模組的名字空間。Python 2.1可以 兩種方式工作,預設地,按Python 2.0的方式工作。但是你可以把下面一行代碼增加到你的模組頭部,使你的模組工作起來象Python 2.2的方式:from __future__ import nested_scopes |
象Python中的許多事情一樣,名字空間在運行時直接可以訪問。特別地,局部名字空間可以通過內建的 locals 函數來訪問。全域(模組層級別)名字空間可以通過 globals 函數來訪問。
例 4.10. locals 介紹
>>> def foo(arg):
... x = 1 ... print locals() ... >>> foo(7)
{'arg': 7, 'x': 1} >>> foo('bar')
{'arg': 'bar', 'x': 1}
|
函數 foo 在它的局部名字空間中有兩個變數:arg(它的值被傳入函數),和 x(它是在函數裡定義的)。 |
|
locals 返回一個名字/值對的字典。這個字典的鍵字是字串形式的變數名字,字典的值是變數的實際值。所以用 7 來調用 foo,會列印出包含函數兩個局部變數的字典:arg (7) 和 x (1)。 |
|
回想一下,Python有動態資料類型,所以你可以非常容易地傳遞給 arg 一個字串,這個函數(和對 locals 的調用)將仍然很好的工作。locals 可以用於所有類型的變數。 |
locals 對局部(函數)名字空間做了些什麼,globals 就對全域(模組)名字空間做了什麼。然而 globals 更令人興奮,因為一個模組的名字空間是更令人興奮的。[9] 不僅僅是模組的名字空間包含了模組層級的變數和常量,它還包括了所有在模組中定義的函數和類。再加上,它包括了任何被匯入到模組中的東西。
回想一下from module import 和 import module之間的不同。使用 import module,模組自身被匯入,但是它保持著自已的名字空間,這就是為什麼你需要使用模組名來訪問它的函數或屬性(module.function)的原因。但是使用 from module import,實際上是從另一個模組中將指定的函數和屬性匯入到你自己的名字空間,這就是為什麼你可以直接存取它們卻不需要引用它們所來源的模組的原因。使用 globals 函數,你會真切地看到這一切的發生。
例 4.11. globals 介紹
把下面的代碼加到 BaseHTMLProcessor.py 中:
if __name__ == "__main__": for k, v in globals().items():
print k, "=", v
|
不要被嚇壞了,想想以前你已經全部都看到過了。globals 函數返回一個字典,我們使用 items 方法和多變數賦值來遍曆字典。在這裡唯一的新東西就是 globals 函數。 |
好,從命令列運行這個指令碼會得到下面的輸出:
c:\docbook\dip\py>python BaseHTMLProcessor.py
SGMLParser = sgmllib.SGMLParser
htmlentitydefs = <module 'htmlentitydefs' from 'C:\Python21\lib\htmlentitydefs.py'>
BaseHTMLProcessor = __main__.BaseHTMLProcessor
__name__ = __main__
[...略...]
|
SGMLParser 使用了 from module import 從 sgmllib 中被匯入。這就是說它被直接匯入到我們的模組名字空間了,就是這樣。is imported from sgmllib, using from module import. That means that it was imported directly into our module's namespace, and here it is./td> |
|
每個模組都有一個 doc string(文檔字串),可以使用內建屬性 __doc__ 來訪問。這個模組沒有明確地定義文檔字串,所以預設為 None。 |
|
這個模組僅定義了一個類,BaseHTMLProcessor,不錯。注意這兒的值就是類本身,不是一個特別的類執行個體。 |
|
記得 if __name__ 技巧嗎?當運行一個模組時(對從另外一個模組中匯入而言),內建的 __name__ 是一個特殊值 __main__。因為我們是把這個模組當作指令碼從命令來啟動並執行,故 __name__ 值為 __main__,這就是為什麼我們這段簡單地列印 globals的代碼可以執行的原因。 |
|
使用 locals 和 globals 函數,通過提供變數的字串名字你可以動態地得到任何變數的值。這種方法反映了 getattr) 函數(它允許你通過提供函數的字串名來動態地訪問任意的函數。)的機理。 |
在 locals 和 globals 之間有另外一個重要的區別,你應該在它困擾你之前就學瞭解它。它無論如何都會困擾你的,但至少你還記得瞭解過它。
例 4.12. locals 是唯讀,globals 不是
def foo(arg): x = 1 print locals()
locals()["x"] = 2
print "x=",x
z = 7 print "z=",z foo(3) globals()["z"] = 8
print "z=",z
|
因為使用 3 來調用 foo,會列印出 {'arg': 3, 'x': 1}。這個應該沒什麼奇怪的。 |
|
你可能認為這樣會改變局部變數 x 的值為 2,但並不會。locals 實際上沒有返回局部名字空間,它返回的是一個拷貝。所以對它進行改變對局部名字空間中的變數值並無影響。 |
|
這樣會列印出 x= 1,而不是 x= 2。 |
|
在有了對 locals 的經驗之後,你可能認為這樣不會改變 z 的值,但是可以。由於Python在實現過程中內部有所區別(關於這些區別我寧可不去研究,因為我自已還沒有完全理解),globals 返回實際的全域名字空間,而不是一個拷貝:與 locals 的行為完全相反。所以對 globals 所返回的字典的任何的改動都會直接影響到全域變數。 |
|
這樣會列印出 z= 8,而不是 z= 7。 |