Python/作用域與名字空間
變量的作用域共有4種:
- L (Local) 局部作用域
- E (Enclosing) 外層非全局的閉包作用域
- G (Global) 全局作用域
- B (Built-in) 內建作用域
按照L –> E –> G –>B 的順序查找變量。
Python 中只有模塊(module)、類(class)、函數(def、lambda)才會引入新的作用域,其它的代碼塊(如 if/elif/else/、try/except、for/while等)不會引入新的作用域,這些語句內定義的變量,在語句的外部也可以訪問。
具體說,
- 每個模塊有自己的全局作用域,可以用globals()訪問。模塊的頂層的局部作用域等同於全局作用域。模塊的全局作用域保存在該模塊對象的__dict__中。
- 函數的局部作用域保存在函數調用棧的棧幀中。即幀對象的「fast locals」結構中。並非普通 dict。locals() 或 frame.f_locals 返回的是一個字典視圖/快照,通常修改它不會回寫到真實局部。
- 類對象的作用域,保存在類對象的__dict__中。
- 類實例的作用域,保存在類實例的__dict__中
類與函數可以閉包(enclosing)。如果在一個內部函數裡,對一個外部作用域(但不是全局作用域)的變量進行引用,那麼內部函數就被稱為閉包(closure),而這個被內部函數引用的變量則被成為自由變量。閉包捕獲的變量不在普通局部字典里,它們存在於函數對象的__closure__(相當於元組)中,每個單元的屬性.cell_contents返回閉包自由變量的值。閉包自由變量的名字存在於函數對象的__code__.co_freevars(相當於元組)中。
global關鍵字指明要訪問全局的變量。nonlocal關鍵字指明要訪問外層非全局作用域的變量。在函數作用域內部,可以讀訪問全局變量,但如果修改則自動被當作是一個同名的局部變量,除非顯式使用global關鍵字。如:
list1 = []
def test3(): list1 = [2]
nonlocal是從Python 3.0引入的關鍵字,允許賦值修改外層非全局的變量。
從Python解釋器的實現看,維持全局名字字典與局部名字字典,可以分別用globals()與locals()列出兩個作用域內的全部名字。對於類或對象實例,還可以用vars(InstanceName)訪問其類或實例作用域內的名字。注意,locals()的值只能讀不能修改。
import模塊名字後,在sys.modules字典中增加「實際導入名」(完全限定名)及模塊所在的包,在當前globals()中增加模塊名字(或者模塊別名)。