Python學習:作用域(namespace)
Python作用域基礎
Python有四個作用域:
L(Local)本地也稱作局部作用域;
E(Enclosing)閉包函數外的函數中;
G(global)全局作用域;
B(Built-in)內建作用域;
變量可以在三個不同的地方分配:
如果一個變量在def內賦值,它被定位在這個函數之內。
如果一個變量在嵌套的def中賦值,對於嵌套的函數來說,它是非本地的。
如果在def之外賦值,它就是整個文件全局的。
值得註意的是,修改一個對象並不是對一個名稱賦值。
變量名解析:LEGB原則
對於一個def語句:
變量名引用分為三個作用域進行查找:首先是本地,之後是函數內(如果有的話),之後全局,最後是內置。L->E->G->B
Python除了def/class/lambda
外,其他如:if/elif/else/ try/except for/while
並不能改變作用域。定義在他們之內的變量,外部還是可以訪問。
>>> if True: ... a = ‘I am A‘ ... >>> a ‘I am A‘ # 定義在if語言中的變量a,外部還是可以訪問的。 # 但是需要註意如果if被 def/class/lambda 包裹,在內部賦值,就變成了此 函數/類/lambda 的局部作用
在def/class/lambda內進行賦值,就變成了其局部作用域。局部作用域會覆蓋全局作用域,但不會影響全局作用域。
g=1 #全局變量 def func(): g = 2 #局部變量 return g print func() #結果為2 print g #結果為1
值得註意的是,有時候想再函數內調用全局變量,疏忽了會報錯,如下:
#file1 var = 1 def func(): print var var = 200 func() #file2 var = 1 def func(): var = var +1 return var func() #這兩個函數都會報錯UnboundLocalError: local variable ‘var‘ referenced before assignment
上述兩個函數都會報同樣的錯誤:為賦值之前引用變量!為什麽?在函數內部,解釋器探測到變量var重新被賦值,所以var變成了局部變量,但是在被賦值之前就使用了var,便會出現這個錯誤。解決的方法是在函數內部添加globals var語句,但運行函數後全局的var也會被修改。
#file1 var = 1 def func(): global var print var var = 200 func() #結果為1 print var #全局變量var變為200 #file2 var = 1 def func(): global var var = var +1 return var print func() #結果為2
閉包Closure
閉包的定義:如果在一個內部函數裏,對外部函數內(不是全局變量)進行引用,那麽內部函數就被認為是閉包(closure)。
a = 1 def external(): global a a = 200 print a b =100 def internal(): print b b = 200 return b internal() print b print external() #一樣會報錯,賦值前引用UnboundLocalError: local variable ‘b‘ referenced before assignment
Python3中有關鍵字nonlocal可以解決這個問題,但在Python2中盡量不要嘗試修改閉包中的變量。
關於閉包,還有一個坑:
from functools import wraps def wrapper(log): def external(F): @wraps(F) def internal(**kw): if False: log = ‘modified‘ print log return internal return external @wrapper(‘first‘) def abc(): pass print abc()
也會出現 引用在賦值之前 的錯誤,原因是解釋器探測到了 if False 中的重新賦值,所以不會去閉包的外部函數(Enclosing)中找變量,但 if Flase
不成立沒有執行,所以便會出現此錯誤。除非你還需要else: log=‘var‘
或者 if True
但這樣添加邏輯語句就沒了意義,所以盡量不要修改閉包中的變量。
好像用閉包無法實現計數器功能,因為在閉包內部count+=1就會出現在賦值前引用的錯誤(Python3用關鍵字nonlocal可以解決)
def counter(start): count = [start] def internal(): count[0] += 1 return count[0] return internal count = counter(0) for n in range(10): print count() #結果分別為1,2,3,4,5,6,7,8,9,10 count = counter(0) print count() #結果為1
global和globals()
global用來在函數內部聲明全局變量,globals()
和 locals()
提供了基於字典的訪問全局和局部變量的方式。
比如:如果函數1內需要定義一個局部變量,名字另一個函數2相同,但又要在函數1內引用這個函數2。
def var(): pass def f2(): var = ‘Just a String‘ f1 = globals()[‘var‘] print var return type(f1) print f2() # Just a String # <type ‘function‘>
Python學習:作用域(namespace)