Python 名稱空間和作用域
在本節中,我們將來學習 Python 名稱空間和命名的作用域。
Python 中的命名
Python 中的命名是賦予物件的識別符號。Python 是一種物件導向的程式語言,這意味著它所有東西都是 Python 中的一個物件,其中命名名稱用來訪問具體的物件。
例如,當你給一個變數 x = 3
賦值時, 這裡 3
是一個物件並儲存在一個記憶體位置(RAM)中。這個記憶體位置是一個被命名的記憶體位置,可以通過名字 x
訪問。該物件 3
的地址可以通過使用內建函式 id()
來獲得。
>>> x = 3
>>> print('id(3) =', id(3))
id(3) = 1864160336
>>> print('id(x) =', id(x))
id(x) = 1864160336
現在這裡的物件 3
儲存在一個名字為 x
的位置中,因此它們的記憶體地址是相同的。
當你將 x
的值賦給某個其他變數時,比如說 y
, 並改變 x
的值,那麼 x
將會有一個新的位置,y
將會有前一個 x
的位置。如下所示:
x = 3
print("id(x) =", id(x))
x = 4
print("id(4) =", id(4))
print("id(x) =", id(x))
y = 3
print("id(y) =", id(y))
id(x) = 1864160336
id(4) = 1864160352
id(x) = 1864160352
id(y) = 1864160336
你可以看得出,y
的記憶體地址等於 x
的舊記憶體地址。
Pythhon 中的名稱空間
Python 名稱空間是命名名稱的集合。名稱空間是名稱到物件的對映,在 Python 中它的資料型別是一個字典。名稱空間確保程式中使用的每個名稱都是唯一的。
名稱空間在直譯器啟動時建立,當程式執行結束時刪除。該名稱空間已經包含所有內建名稱,因此,無論何時需要內建函式(例如 id()
),都可以直接在程式中使用它。
當你呼叫一個函式時,會建立一個包含所有已定義名稱的本地名稱空間。
Python 名稱空間,可以分為三種型別:
- 本地名稱空間 - 在某個函式或者類方法裡面的本地名稱集合。
- 全域性名稱空間 - 在當前模組下的命名集合
- 內建名稱空間 - Python 內建命名集合
下面我們來舉幾個例子,來具體說明下它們的含義。
Python 名稱空間-本地名稱空間
>>> def localTest(arg):
param = 2
print(locals())
>>> localTest(1)
{'param': 2, 'arg': 1}
locals()
就是我們上面說的本地名稱空間,它是一個 Python 字典物件。其中,鍵是命名,裡面也包括了輸入引數,值是具體命名所對應的資料。
假如函式中既沒有輸入引數,也沒有本地區域性變數,那它的本地名稱空間是什麼呢?
自己來試試吧。
Python 名稱空間-全域性名稱空間
x = 1
y = 2
stringX = "String X"
def globalTest():
for (key, value) in globals().items():
print("{} - {}".format(key, value))
globalTest()
__name__ - __main__
__doc__ - None
__package__ - None
__loader__ - <_frozen_importlib_external.SourceFileLoader object at 0x0000020146FED630>
__spec__ - None
__annotations__ - {}
__builtins__ - <module 'builtins' (built-in)>
__file__ - NameSpace_GlobalNameSpcae.py
__cached__ - None
x - 1
y - 2
stringX - String X
globalTest - <function globalTest at 0x0000020146F91E18>
從結果我們可以看出,全域性名稱空間裡包括了整個模組內的命名名稱,比如變數 x
,y
,stringX
,函式名 globalTest
,而且還包括了一些內建的變數命名,比如 __name__
, __file__
等等。
Python 中變數作用域
在程式中建立變數時,你可能無法從程式的每個地方都能訪問該變數,這是因為變數的作用域的存在。你試著訪問名稱空間中未定義的變數時候,就會得到系統報錯 UnboundLocalError:local variable referenced before assignment
。
作用域可以被定義為你可以在沒有任何字首的情況下訪問你的名稱空間的這樣的一個域或者範圍。
作用域可以分類為:
L
- 區域性作用域 Scope of a function where you have local names。E
-enclosing scope
,閉包函式作用域,也就是在巢狀函式中的作用域 Scope of a module where you have global variables。G
- 全域性作用域B
- 內建作用域
當在函式中引用一個變數的時候,以上的作用域順序也是 Python 搜尋的一個順序 L->E-G-B
,首先嚐試區域性作用域,沒找到的話,繼續搜尋閉包函式作用域,然後再是全域性作用域和內建作用域。
接下來,我們用一個例子來說明 Python 變數作用域的細節。
outer = "global variable"
def searchOrderFunc():
enclosing = "enclosing variable"
def searchOrderFuncInner():
inner = "inner variable"
print(inner) # fetch from (L)ocal scope
print(enclosing) # fetch from (E)nclosing scope
print(outer) # fetch from (G)lobal scope
print(any) # fetch from (B)uilt-ins
searchOrderFuncInner()
searchOrderFunc()
inner variable
enclosing variable
global variable
<built-in function any>