在python中,變量查找遵循LGB原則,即優先在局部作用域(local scope)中對變量進行查找,失敗則在全局作用域(global scope)中進行查找,最後嘗試再內建作用域(build-in scope)內查找,如果還是未找到的話,則拋出異常。後來由於閉包和嵌套函數的出現,作用域又增加了外部作用域,這樣變量的查找作用域優先級變為:局部、外部、全局和內建。 作用域由def、class、lambda等語句產生,if、try、for等語句並不會產生新的作用域。變量名引用分為三個作用域進行查找:首先是本地,然後是函數內(如果有的話),之後是全局,最後是內置。在默認情況下,變量名賦值會創建或者改變本地變量。全局聲明將會給映射到模塊文件內部的作用域的變量名賦值。Python 的變量名解析機制也稱為 LEGB 法則,具體如下:當在函數中使用未確定的變量名時,Python 搜索 4 個作用域:本地作用域(L),之後是上一層嵌套結構中 def 或 lambda 的本地作用域(E),之後是全局作用域(G),最後是內置作用域(B)。按這個查找原則,在第一處找到的地方停止。如果沒有找到,Python 會報錯的。
a = 1
def f():
a = 2
def g():
print a //[1]:輸出結果為2
return g
func = f()
func()//[2]
代碼的[2]處調用的函數實際上調用的是函數f 中定義的內嵌函數g,在代碼 的[1]處,函數g 內的“print a”的輸出結果為2。初看上去有些疑問,因為函數f 內的約束“a = 2”在其之外應該是不起作用的,當執行func()時,起作用的約束應該是“a = 1”才對。但是我們之前說到了,作用域僅僅是由文本決定的,函數g 位於函數f 之內,所以函數g 定義的作用域內嵌於函數f 的作用域之內。換句話說,函數f 的作用域是函數g 的作用域的直接外圍作用域,所以,按照最內嵌套作用域規則,[1]處的名字引用應該引用的是函數f 定義的作用域中所創建的約束。
盡管在代碼清單8-2 的[2]處,“a = 2”這個約束已經不起作用了,但是Python 在執行“func = f()”時,會執行函數f 中的“def g():”語句,這時Python 會將約束“a = 2”與函數g 對應的函數對象捆綁在一起,將捆綁後的結果返回,這個捆綁起來的整體被稱為“閉包”。
實際上這裡有一個相當微妙的問題,最內嵌套作用域規則是“閉包”的結果呢,還是“閉包”是最內嵌套作用域規則的實現方案?這兩個問題看上去是一致的,但卻隱含著誰決定誰的關系。實際上,Python 實現閉包是為了實現最內嵌套作用域規則。換句話說,最內嵌套作用域規則是語言設計時的設計策略,即是形而上的“道”;而閉包則實現語言時的一種方案,即是形而下的“器”。
python能夠改變變量作用域的代碼段是def、class、lamda.
def scopetest():
localvar=6;
print(localvar)
scopetest()
#print(localvar) #去除注釋這裡會報錯,因為localvar是本地變量
if/elif/else、try/except/finally、for/while
while True:
newvar=8
print(newvar)
break;
print(newvar)
try:
newlocal=7
raise Exception
except:
print(newlocal)#可以直接使用哦
輸出結果:8 8 7
可見這個關鍵字中定義變量,他們的作用域跟外部是一致的,這個跟Java的作用域概念有點不一樣。
變量搜索路徑是:本地變量->全局變量
def scopetest():
var=6;
print(var)#
var=5
print(var)
scopetest()
print(var)
輸出結果:5 6 5
這裡var 首先搜索的是本地變量,scopetest()中 var=6相當於自己定義了一個局部變量,賦值為6. 當然如果的確要修改全局變量的值,則需要如下:
def scopetest():
global var
var=6;
print(var)#
var=5
print(var)
scopetest()
print(var)
輸出結果:5 6 6
再看一種這種情況:
def scopetest():
var=6;
print(var)#
def innerFunc():
print(var)#look here
innerFunc()
var=5
print(var)
scopetest()
print(var)
輸出結果:5 6 6 5