做者:zhbzz2007 出處:http://www.cnblogs.com/zhbzz2007 歡迎轉載,也請保留這段聲明。謝謝!python
你必定在不少計算機科學課程上據說過做用域。它很重要,若是你不理解它的工做原理,那麼就會出現一些使人困惑的錯誤。做用域最基本的功能就是告訴編譯器一個變量何時是可見的。也就是說,做用域定義了你使用變量的時間和範圍。當你嘗試使用一些不在當前做用域的變量時,你就會獲得NameError。閉包
Python有三類做用域:函數
局部做用域是Python中使用最多的做用域。當你在一段代碼塊中建立一個變量,它將會在最近的做用域中使用。全部的做用域組成的集合就是代碼塊環境。也就是說,默認是在局部做用域中處理全部的任務。若是你想要不一樣的做用域,那麼你須要將變量設置爲全局做用域或非局部做用域。學習
如今,咱們使用Python的解釋器建立一個簡單的例子,來展現局部做用域任務。調試
>>> x = 10 >>> def my_func(a,b): ... print(x) ... print(z) ... >>> my_func(1,2) 10 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in my_func NameError: global name 'z' is not defined
這裏,咱們建立了變量x以及一個入參爲兩個參數的簡單函數。它將會打印x和z。請記住,咱們尚未定義z,因此當咱們調用這個函數時,咱們將會得到NameError。這是由於z尚未定義或者它在做用域外部。若是你在調用函數以前定義z,那麼就會發現z,你就不會得到NameError。code
若是你嘗試訪問函數內部的變量,那麼你也會得到NameError。blog
>>> def my_func(a,b): ... i = 2 ... print(x) ... >>> if __name__ == "__main__": ... x = 10 ... my_func(1,2) ... print(i) ... 10 Traceback (most recent call last): File "<stdin>", line 4, in <module> NameError: name 'i' is not defined
變量i僅僅在函數內部定義,因此當你運行這段代碼時,你將會獲得NameError。作用域
咱們稍微修改一下上面的代碼,將下面的代碼存儲到文件中,並運行。rem
def my_func(a,b): x = 5 print(x) if __name__ == "__main__": x = 10 my_func(1,2) print(x)
你認爲將會發生什麼?10打印兩次?實際上並非。緣由就是咱們如今有兩個x變量。my_func函數中的變量x是局部函數做用域,它將會覆蓋函數外部的變量x。當咱們調用my_func函數時,咱們打印5而非10。當函數返回時,my_func函數中的變量x會被回收,函數外的變量x將會起做用,這就是爲何最後一行語句打印出10。get
若是你想了解具體的技巧,你能夠在函數中的賦值語句前打印x,以下所示,
>>> def my_func(a,b): ... print(x) ... x = 5 ... print(x) ... >>> if __name__ == "__main__": ... x = 10 ... my_func(1,2) ... print(x)
當你運行這段代碼時,你將會獲得以下的異常,
Traceback (most recent call last): File "<stdin>", line 3, in <module> File "<stdin>", line 2, in my_func UnboundLocalError: local variable 'x' referenced before assignment
這個異常發生,是由於Python提示你在my_func函數中後賦值給x,因爲x尚未定義,所以它拋出異常。
Python包括global語句。它是Python的一個關鍵字。global語句定義了這個變量能夠在隨後的代碼塊中做爲變量使用。雖然你能夠在聲明全局以前建立一個名稱,但這是很是不鼓勵的。讓咱們嘗試使用global來修復上一個例子拋出的異常:
def my_func(a,b): global x print(x) x = 5 print(x) if __name__ == "__main__": x = 10 my_func(1,2) print(x)
這段代碼的輸出是,
10 5 5
經過將x定義爲全局變量,咱們告訴Pyton在函數中第一個打印函數中首先使用第一個定義的x。而後咱們給x賦予新值5,在退出函數前再次打印。你將會注意到如今x是全局變量,當咱們到達代碼塊的最後一個輸出語句時,x依然是5。
讓咱們混合使用global和local來作一些有意思的事情,
def my_func(a,b): global c b,a = a,b d = 'Mike' print(a,b,c,d) a , b , c , d = 1 , 2 , 'c is global' , 4 my_func(a,b) print(a , b , c , d)
在這裏,咱們將變量c設置爲全局變量。這個將會致使在函數內部和外部,c都會輸出相同的值。咱們在函數內部交換變量a和b,能夠顯示出咱們在函數內部對其進行了交換,可是在函數外部,並無修改兩者。這也顯示出變量a和b並非全局變量,你應該能夠看到以下的輸出結果:
(2, 1, 'c is global', 'Mike') (1, 2, 'c is global', 4)
我在此想提醒你不要在函數內部修改全局變量。這在Python社區中是一個很差的例子,它也會致使調試更加困難。
如今,咱們已經理解了局部和全局變量,下面咱們將要了解非局部變量。
Python 3新增了一個關鍵詞--nonlocal。關鍵詞nonlocal增長了一個做用域用於覆蓋內部做用域。你能夠閱讀PEP 3104。下面一段代碼能夠很好的解釋非局部做用域。最多見的例子就是建立一個自增函數,
def counter(): num = 0 def incrementer(): num += 1 return num return incrementer
若是你運行這段代碼,你將會獲得UnboundLocalError這個錯誤,由於變量num在內部函數中在賦值以前引用。讓咱們增長局部做用域,
>>> def counter(): ... num = 0 ... def incrementer(): ... nonlocal num ... num += 1 ... return num ... return incrementer ... >>> >>> c = counter() >>> c <function counter.<locals>.incrementer at 0x7f67735ffea0> >>> c() 1 >>> c() 2 >>> c() 3
如今咱們定義的自增函數已按照咱們指望開始工做。這種類型的函數被稱爲closure(閉包)。閉包就是一個將非局部變量封裝起來的代碼塊。閉包背後的思想就是你能夠在函數外部引用這些變量。
nonlocal容許你將變量分配到做用域外,但不是全局做用域。你不能在counter函數中使用nonlocal,由於它嘗試着將其分配到全局做用域。你能夠嘗試一下,你將會獲得SyntaxError。因此你必須在嵌套函數中使用nonlocal。
在本文中,咱們瞭解了經過Python關鍵詞global和nonlocal來修改變量的引用方式。咱們學習了在哪裏使用以及爲何。咱們也學習了局部做用域。