標籤:活動 var if語句 變數賦值 not UNC 綁定 int() ***
Python 變數範圍
變數範圍LEGB
1、變數的範圍
在Python程式中建立、改變或尋找變數名時,都是在一個儲存變數名的空間(命名空間)中進行的,我們稱這個命名空間為範圍。Python的範圍是靜態,在原始碼中變數名被賦值的位置決定了該變數能被訪問的範圍,即Python變數的範圍由變數所在原始碼中的位置決定。
2、進階語言對資料類型的使用過程
一般的進階語言在使用變數時,都會有下面4個過程,當然不同的語言也會有著區別:
- 1.聲明變數:讓編譯器知道由這個變數的存在;
- 2.定義變數:為不同資料類型的變數分配記憶體空間;
- 3.初始化:賦值,填充分配好的記憶體空間;
- 4.引用:通過引用對象(變數名)來調用記憶體對象(記憶體資料);
3、範圍的產生
就範圍而言,Python與C有著很大的區別,在Python中並不是所有的語句塊都會產生範圍。只有當變數在Module(模組)、Class(類)、def(函數)中定義的時候,才會有範圍的概念。如下代碼:
def func(): variable = 100 print(variable)print(variables) # NameError: name ‘variables‘ is not defined,在pycharm中就會自動報錯
在範圍中定義的變數,一般只在範圍中有效。需要注意的是:在if-elif-else、for-else、while、try-except/try-finally等關鍵字的語句塊中並不會產生範圍,如下代碼:
if True: variable = 100 print(variable) # 100print("*"*12) # ************print(variable) # 100
所以,可以看到,雖然在if語句中定義的variable變數,但是在if語句外仍然能夠使用。
4、範圍的類型:
在Python中,使用一個變數時並不嚴格要求預先聲明它,但是在真正使用它之前,他必須被綁定到某個記憶體對象(被定義、賦值);這種變數名的綁定將在當前範圍中引入新的變數,同事屏蔽外層範圍中的同名變數。
L(local)局部變數範圍
局部變數:包含在def關鍵字定義的語句塊中,即在函數中定義的變數。每當函數被調用時都會建立一個新的局部範圍。Python中也有遞迴,即自己調用自己,每次調用都會建立一個新的局部命名空間。在函數內部的變數聲明,除非特別的聲明為全域變數,否則均預設為局部變數。有些情況需要在函數內部定義全域變數,這時可以使用global關鍵字來聲明變數的範圍為全域。局部變數域就像一個棧,僅僅是暫時的存在,依賴建立該局部範圍的函數是否處於活動的狀態。所以,一般建議盡量少用全域變數,因為全域變數在模組檔案啟動並執行過程中會一直存在,佔用記憶體空間。
注意:如果需要在函數內部對全域變數賦值,需要在函數內部通過global語句聲明該變數為全域變數。
E(enclosing)嵌套範圍
E也包含在def關鍵字中,E和L是相對的,E相對於更上層的函數而言也是L。與L的區別在於,對一個函數而言,L是定義在此函數內部的局部範圍,而E是定義在此函數的上一層父級函數的局部範圍,主要是為了實現Python的閉包,而增加的實現。
G(global)全域範圍
即在模組層次中定義的變數,每一個模組都是一個全域範圍。也就是說,在模組檔案頂層聲明的變數具有全域範圍,從外部來看,模組的全域變數就是一個模組對象的屬性。
注意:全域範圍的作用範圍僅限於單個模組檔案內。
B(built-in)內建範圍
系統內固定模組裡定義的變數,如與定義在builtin模組內的變數。
5、變數名解析LEGB法則
搜尋變數名的優先順序:局部範圍-> 嵌套範圍-> 全域範圍-> 內建範圍
LEGB法則:當在函數中使用未確定的變數名時,Python會按照優先順序依次搜尋4個範圍,以此來確定該變數名的意義。首先搜尋局部範圍(L),之後是上一層嵌套結構中def或lambda函數的嵌套範圍(E),之後是全域範圍(G),最後是內建範圍(B)。按照這個尋找原則,在第一處找到的地方停止,如果沒找到,則會拋出NameError異常。如下執行個體:
def func(): variable = 300 print(variable) # 300variable = 100func()print(variable) # 100
variable = 100def test_scope(): print(variable) # variable是test_scope()的局部變數,但是在列印時並沒有綁定記憶體對象,所以在pycharm中就會報錯 variable = 200test_scope()print(variable)
上面的例子會產生錯誤,因為在執行程式的時的先行編譯能夠在test_scope()中找到局部變數variable(對variable進行了賦值)。在局部範圍找到了變數名,所以不會升級到嵌套範圍去尋找,但是在使用print()將變數variable列印時,局部變數並沒有綁定到一個記憶體對象(沒有定義和初始化,即沒有賦值)。本質上還是Python調用變數時遵循的LEGB法則和Python解譯器的編譯原理,決定了這個錯誤的發生。所以,在調用一個變數之前,需要為該變數賦值(綁定一個記憶體對象)。
注意:為什麼在這個例子中觸發的錯誤是"UnboundLocalError: local variable ‘variable‘ referenced before assignment",而不是"NameError:name ‘variable’ is not defined"。這是因為變數variable不是在全域範圍。Python中的模組代碼在執行前,並不會經過先行編譯,但是模組內的函數體代碼在運行前會經過先行編譯,因此不管變數名的綁定發生在範圍的哪個位置,都能被編譯器知道。Python雖然是一個靜態範圍語言,但變數名尋找是動態發生的,直到在程式運行時,才會發現範圍方面的問題。
Python 變數範圍