Python 作用域解析之補遺

pwpb6678 8年前發布 | 7K 次閱讀 Python Python開發

有人把 Python 作用域解析規則總結為 LEGB , 確實夠精煉簡要。但我還要繼續補遺:

其一,所謂 global 命名空間,都是隸屬某模塊名的。于是當解釋執行(注意沒有加 -m 參數)某 Python 源代碼文件,或在交互式環境里,這個文件或交互式環境本身所謂的 global 命名空間,其實同樣隸屬一個模塊,它的名字就叫 __main__ . 如果您在被解釋執行的文件或交互式環境定義一個函數對象 a , 那么 print(a.__module__) 會返回一個 str 對象,其值為 __main__ . 說來好笑,我曾經納悶 有什么辦法能訪問到這模塊 ,后來想想,其實我本來不就在文件和交互式環境本身里面啊,當然能隨便直接訪問了。

其二, import a , print(a.b) 很好理解,會先在 __main__ 模塊解析到 a 這模塊的名字,接著就在后者的 global 命名空間繼續解析到 b .

其三, print(sum.__module__) , def sum(): pass , print(sum.__module__) 會先后輸出 __builtins__ 和 __main__ , 但這不是因為 rebinding 了 builtins 命名空間的 sum ,而是在 __main__ 模塊的 global 命名空間定義了 sum 對象且后者被先解析到而已。此外您也可以 把 __builtins__ 當成模塊對象(注意不是 str 對象)來訪問它 global 命名空間的值 ,比如 print(__builtins__.__dict__) 就返回一個字典對象,后者把 str 對象映射為您可以訪問的內置對象。說起來 其實這里很微妙 ,因為 __builtins__ 本身應該被解析為 __main__ 模塊 global 命名空間的某對象了,但您并沒有 import 它, 也就是說它從一開始就默認存在,但偏偏又不隸屬「如同字面意義上所真正內置」的 __builtins__ 模塊的 global 命名空間! 事實上您還可以顯式 import builtins , 再繼續訪問它的屬性……比如 print(builtins.__dict__) 就返回和 print(__builtins__.__dict__) 一樣的值。

其四, del 可以在 __main__ 的命名空間刪掉對庫的引用 ,比如 import sys 后再 del sys , 您會發現 sys 庫又訪問不到了,可謂變相的 unimport. 那么 del __builtins__ 會發生什么? 您在 __main__ 模塊的 global 命名空間刪掉了對 __builtins__ 的引用**,您自然沒法用 int , str 之類的內置對象了,而且連 import builtins as __builtins__ 之類的補救方案都執行不了,因為連 __import__ 都找不到了。何等喪心病狂的 hack!

其五, from a import * 應該會直接把 a 模塊所有 global 變量歸屬到 __main__ 模塊的 global 命名空間,但是您如果試試輸出模塊 m 里的 b 函數所屬的模塊名的話,即 print(b.__module__) , 它返回 m 而不是 __main__ ; 此外,如果 m 和 n 各有函數 b , 那先后 from m import * from n import * 的話,那 print(b.__module__) 返回 n . 我把這災難叫做 作用域偏移

最后總結編程規范:

  1. 禁止一切 from * import * 形式的語句
  2. 不要亂動一切以下劃線開頭的東西,自然包括 __builtins__ .
  3. 通過 del 來 umimport 庫。
  4. 不要在 local 或 global 空間命名和 builtin 空間某對象重復的 identifier, 可以在名字后面加一個下劃線以區分,比如 str_ .

 

via: http://tech.acgtyrant.com/Python-作用域解析之補遺/

 本文由用戶 pwpb6678 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!