Python基礎-Python:locals 和 globals

Python有兩個內置的函數,locals 和globals,它們提供了基於字典的訪問局部和全局變量的方式。
首先,是關於名字空間的一個名詞解釋。是枯燥,但是很重要,所以要耐心些。Python使用叫做名字空間的東西來記錄變量的軌跡。名字空間只是一個字典,它的鍵字就是變量名,字典的值就是那些變量的值。實際上,名字空間可以象Python的字典一樣進行訪問,一會我們就會看到。
在一個Python程序中的任何一個地方,都存在幾個可用的名字空間。每個函數都有着自已的名字空間,叫做局部名字空間,它記錄了函數的變量,包括函數的參數和局部定義的變量。每個模塊擁有它自已的名字空間,叫做全局名字空間,它記錄了模塊的變量,包括函數、類、其它導入的模塊、模塊級的變量和常量。還有就是內置名字空間,任何模塊均可訪問它,它存放着內置的函數和異常。
當一行代碼要使用變量 x 的值時,Python會到所有可用的名字空間去查找變量,按照如下順序:
局部名字空間 - 特指當前函數或類的方法。如果函數定義了一個局部變量 x,Python將使用這個變量,然後停止搜索。
全局名字空間 - 特指當前的模塊。如果模塊定義了一個名爲 x 的變量,函數或類,Python將使用這個變量然後停止搜索。
內置名字空間 - 對每個模塊都是全局的。作爲最後的嘗試,Python將假設 x 是內置函數或變量。
如果Python在這些名字空間找不到 x,它將放棄查找並引發一個 NameError 的異常,同時傳 遞 There is no variable named 'x' 這樣一條信息,回到第一章,你會看到一路上都有這樣的信息。但是你並沒有體會到Python在給出這樣的錯誤之前做了多少的努力。
Important
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): 1 ... x = 1 ... print locals() ...  >>> foo(7) 2 {'arg': 7, 'x': 1} >>> foo('bar') 3 {'arg': 'bar', 'x': 1}
1 函數 foo 在它的局部名字空間中有兩個變量:arg(它的值被傳入函數),和 x(它是在函數裏定義的)。
2 locals 返回一個名字/值對的字典。這個字典的鍵字是字符串形式的變量名字,字典的值是變量的實際值。所以用 7 來調用foo,會打印出包含函數兩個局部變量的字典:arg (7) 和 x (1)。
3 回想一下,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(): 1 print k, "=", v
1 不要被嚇壞了,想想以前你已經全部都看到過了。globals 函數返回一個字典,我們使用 items 方法和多變量賦值來遍歷字典。在這裏唯一的新東西就是 globals 函數。
好,從命令行運行這個腳本會得到下面的輸出:
c:\docbook\dip\py>python BaseHTMLProcessor.py
SGMLParser = sgmllib.SGMLParser 1 htmlentitydefs = <module 'htmlentitydefs' from 'C:\Python21\lib\htmlentitydefs.py'> 2 BaseHTMLProcessor = __main__.BaseHTMLProcessor 3 __name__ = __main__ 4 [...略...]
1 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>
2 每個模塊都有一個 doc string(文檔字符串),可以使用內置屬性 __doc__ 來訪問。這個模塊沒有明確地定義文檔字符串,所以缺省爲 None。
3 這個模塊僅定義了一個類,BaseHTMLProcessor,不錯。注意這兒的值就是類本身,不是一個特別的類實例。
4 記得 if __name__ 技巧嗎?當運行一個模塊時(對從另外一個模塊中導入而言),內置的 __name__ 是一個特殊值 __main__。因爲我們是把這個模塊當作腳本從命令來運行的,故 __name__ 值爲 __main__,這就是爲什麼我們這段簡單地打印 globals的代碼可以執行的原因。
Note
使用 locals 和 globals 函數,通過提供變量的字符串名字你可以動態地得到任何變量的值。這種方法反映了 getattr) 函數(它允許你通過提供函數的字符串名來動態地訪問任意的函數。)的機理。
在 locals 和 globals 之間有另外一個重要的區別,你應該在它困擾你之前就學瞭解它。它無論如何都會困擾你的,但至少你還記得了解過它。
例 4.12. locals 是隻讀的,globals 不是
def foo(arg): x = 1 print locals() 1 locals()["x"] = 2 2 print "x=",x 3 z = 7 print "z=",z foo(3) globals()["z"] = 8 4 print "z=",z 5
1 因爲使用 3 來調用 foo,會打印出 {'arg': 3, 'x': 1}。這個應該沒什麼奇怪的。
2
你可能認爲這樣會改變局部變量 x 的值爲 2,但並不會。locals 實際上沒有返回局部名字空間,它返回的是一個拷貝。所以對它進行改變對局部名字空間中的變量值並無影響。
3 這樣會打印出 x= 1,而不是 x= 2。
4
在有了對 locals 的經驗之後,你可能認爲這樣不會改變 z 的值,但是可以。由於Python在實現過程中內部有所區別(關於這些區別我寧可不去研究,因爲我自已還沒有完全理解),globals 返回實際的全局名字空間,而不是一個拷貝:與 locals 的行爲完全相反。所以對 globals 所返回的字典的任何的改動都會直接影響到全局變量。
5 這樣會打印出 z= 8,而不是 z= 7。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章