python locals和globals

locals和globals

標記一下:Dive Into Python 內容 html

咱們先偏離一下 HTML 處理的主題, 討論一下 Python 如何處理變量。 Python 有兩個內置的函數,locals和globals, 它們提供了基於 dictionary 的訪問局部和全局變量的方式。 python

還記得locals嗎? 您第一次是在這裏看到的: app

def unknown_starttag(self, tag, attrs):
        strattrs = "".join([' %s="%s"' % (key, value) for key, value in attrs])
        self.pieces.append("<%(tag)s%(strattrs)s>" % locals())

不, 等等, 此時您還不能理解locals。首先, 您須要學習關於命名空間的知識。這很枯燥, 可是很重要, 所以要要耐心些。 函數

Python 使用叫作名字空間的東西來記錄變量的軌跡。名字空間只是一個 dictionary ,它的鍵字就是變量名,它的值就是那些變量的值。實際上,名字空間能夠象 Python 的 dictionary 同樣進行訪問,一會咱們就會看到。 學習

在一個 Python 程序中的任何一個地方,都存在幾個可用的名字空間。每一個函數都有着自已的名字空間,叫作局部名字空間,它記錄了函數的變量,包括函數的參數和局部定義的變量。每一個模塊擁有它自已的名字空間,叫作全局名字空間,它記錄了模塊的變量,包括函數、類、其它導入的模塊、模塊級的變量和常量。還有就是內置名字空間,任何模塊都可訪問它,它存放着內置的函數和異常。 spa

當一行代碼要使用變量x的值時,Python 會到全部可用的名字空間去查找變量,按照以下順序: .net

  1. 局部名字空間 - 特指當前函數或類的方法。若是函數定義了一個局部變量x, 或一個參數x,Python 將使用它,而後中止搜索。
  2. 全局名字空間 - 特指當前的模塊。若是模塊定義了一個名爲x的變量,函數或類,Python 將使用它而後中止搜索。
  3. 內置名字空間 - 對每一個模塊都是全局的。做爲最後的嘗試,Python 將假設x是內置函數或變量。

若是 Python 在這些名字空間找不到x,它將放棄查找並引起一個NameError異常,同時傳 遞There is no variable named 'x'這樣一條信息,回到 例 3.18 「引用未賦值的變量」,您會看到一路上都有這樣的信息。可是您並無體會到 Python 在給出這樣的錯誤以前作了多少的努力。 命令行

重要
Python 2.2 引入了一種略有不一樣但重要的改變,它會影響名字空間的搜索順序: 嵌套的做用域。 在 Python 2.2 版本以前,當您在一個嵌套函數lambda函數 中引用一個變量時,Python 會在當前 (嵌套的或lambda) 函數的名字空間中搜索,而後在模塊的名字空間。Python 2.2 將只在當前 (嵌套的或lambda) 函數的名字空間中搜索,而後是在父函數的名字空間中搜索,接着是模塊的名字空間中搜索。Python 2.1 可 以兩種方式工做,缺省地,按 Python 2.0 的方式工做。可是您能夠把下面一行代碼增長到您的模塊頭部,使您的模塊工做起來象 Python 2.2 的方式:
 from __future__ import nested_scopes

您是否爲此而感到困惑? 不要絕望! 我敢說這一點很是酷。象 Python 中的許多事情同樣,名字空間 在運行時直接能夠訪問。怎麼樣? 不錯吧,局部名字空間能夠經過內置的locals函數來訪問。全局 (模塊級別) 名字空間能夠經過內置的globals函數來訪問。 code

例 8.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返回一個名字/值對的 dictionary。這個 dictionary 的鍵字是字符串形式的變量名字,dictionary 的值是變量的實際值。因此用7來調用foo,會打印出包含函數兩個局部變量的 dictionary:arg(7) 和x(1)。
3 回想一下,Python 有動態數據類型,因此您能夠很是容易地傳遞給arg一個字符串,這個函數 (和對locals的調用) 將仍然很好的工做。locals能夠用於全部類型的變量。

locals對局部 (函數) 名字空間作了些什麼,globals就對全局 (模塊) 名字空間作了什麼。然而globals更使人興奮,由於一個模塊的名字空間是更使人興奮的。[3] 不只僅是模塊的名字空間包含了模塊級的變量和常量,它還包括了全部在模塊中定義的函數和類。再加上,它包括了任何被導入到模塊中的東西。 htm

回想一下 from module importimport module 之間的不一樣。使用import module,模塊自身被導入,可是它保持着自已的名字空間,這就是爲何您須要使用模塊名來訪問它的函數或屬性:module.function的緣由。可是使用from module import,其實是從另外一個模塊中將指定的函數和屬性導入到您本身的名字空間,這就是爲何您能夠直接訪問它們卻不須要引用它們所來源的模塊的緣由。使用globals函數,您會真切地看到這一切的發生。

例 8.11. globals介紹

看看下面列出的在文件BaseHTMLProcessor.py尾部的代碼塊:

 if __name__ == "__main__": for k, v in globals().items(): 1 print k, "=", v
1 不要被嚇壞了,想一想之前您已經所有都看到過了。globals函數返回一個 dictionary,咱們使用items方法和多變量賦值遍歷 dictionary。在這裏惟一的新東西就是globals函數。

如今從命令行運行這個腳本會獲得下面的輸出 (注意您的輸出可能有略微的不一樣, 這依賴於您的系統平臺和所安裝的 Python 版本):

c:\docbook\dip\py>python BaseHTMLProcessor.py
SGMLParser = sgmllib.SGMLParser 1 htmlentitydefs = <module 'htmlentitydefs' from 'C:\Python23\lib\htmlentitydefs.py'> 2 BaseHTMLProcessor = __main__.BaseHTMLProcessor 3 __name__ = __main__ 4 ...略...
1 SGMLParser使用了from module import從sgmllib中被導入。也就是說它被直接導入到咱們的模塊名字空間了,就是這樣。
2 對比這個和htmlentitydefs, 它是用import被導入的。 也就是說htmlentitydefs模塊自己也在名字空間中, 可是entitydefs變量定義在htmlentitydefs以外。
3 這個模塊只定義一個類,BaseHTMLProcessor, 不錯。 注意這兒的值就是類自己,不是一個特別的類實例。
4 記得 if __name__技巧 嗎?當運行一個模塊時 (對從另一個模塊中導入而言) ,內置的__name__是一個特殊值__main__。由於咱們是把這個模塊看成腳本從命令來運行的,故__name__值爲__main__,這就是爲何咱們這段簡單地打印globals的代碼能夠執行的緣由。
注意
使用locals和globals函數,經過提供變量的字符串名字您能夠動態地獲得任何變量的值。這種方法提供了這樣的功能: getattr 函數容許您經過提供函數的字符串名來動態地訪問任意的函數。

在locals與globals之間有另一個重要的區別,您應該在它困擾您以前就瞭解它。它不管如何都會困擾您的,但至少您還記得了解過它。

例 8.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 locals是一個返回 dictionary 的函數, 而且在 dictionary 中設置一個值。您可能認爲這樣會改變局部變量x的值爲2,但並不會。locals實際上沒有返回局部名字空間,它返回的是一個拷貝。因此對它進行改變對局部名字空間中的變量值並沒有影響。
3 這樣會打印出x= 1,而不是x= 2。
4 在有了對locals的經驗以後,您可能認爲這樣 不會 改變z的值,可是能夠。因爲 Python 在實現過程當中內部有所區別 (關於這些區別我寧肯不去研究,由於我自已尚未徹底理解) ,globals返回實際的全局名字空間,而不是一個拷貝: 與 locals 的行爲徹底相反。因此對 globals 所返回的 dictionary 的任何的改動都會直接影響到全局變量。
5 這樣會打印出z= 8,而不是z= 7。

Footnotes

[3] 我沒有說得太多吧。

相關文章
相關標籤/搜索