1. 程式人生 > >詳細舉例講解 python 的 global 和 nonlocal 的區別

詳細舉例講解 python 的 global 和 nonlocal 的區別

區別

兩個關鍵詞都用於允許在一個區域性作用域中使用外層的變數。

  • global 表示將變數宣告為全域性變數
  • nonlocal 表示將變數宣告為外層變數(外層函式的區域性變數,而且不能是全域性變數)

原理

1、 python 在訪問一個變數時,先要去定位這個變數來源於哪裡。
python引用變數的順序如下:

  1. 當前作用域區域性變數
  2. 外層作用域變數
  3. 當前模組中的全域性變數
  4. python內建變數

即優先從區域性作用域中查詢這個變數,如果沒有的話,再去外層找,如果到了最後還沒找到,則報錯。

2、python 在新建一個變數時,預設作用域為當前區域性作用域。

總結與例項

1、內部函式,不修改外層變數可以訪問外層變數。

a = 1
def fun():
    print(a) # 在函式內部找不到對 a 的定義,則去外層查詢。輸出1。
fun()

2、內部函式,修改同名外層變數,則python會認為是定義了一個新的區域性變數,與外層變數無關了。

a = 1
def fun():
    a = 2 # 聲明瞭一個區域性變數,與外面等於1的那個a沒有關係了
    print(a) # 輸出2
fun()
print(a) # 輸出1

3、在內部函式修改同名全域性變數之前呼叫變數名稱,則引發Unbound-LocalError

a = 1
def fun():
    print(a) # 先引用
a = 2 # 再修改 fun()

報錯:

File “C:\Users\GongYanshang\Desktop\untitled-1.py”, line 3, in fun
print(a)
builtins.UnboundLocalError: local variable ‘a’ referenced before assignment

錯誤表示,區域性變數 a 在定義之前就引用了。
關鍵在於,報錯的是 print(a) 這一行。結合第1個例子,只是增加了一行 a = 2,但是卻是 print(a) 報錯。這說明在 print(a) 之前,python 已經知道了 a 是一個區域性變數,只能從區域性作用域中查詢。從哪裡知道的? 從 a = 2 這一行知道的。
這時候,如果我們想要強制訪問外層變數 a,便可以使用 global 關鍵字:

a = 1
def fun():
    global a # a為全域性變數
    print(a) # 輸出1
    a = 2 # 改變的是全域性變數,因此出了這個區域性作用域,仍然有效
fun()
print(a) # 輸出2

注意,這個時候,不要將 global 換為 nonlocal 關鍵字,不然會報錯:

Syntax Error: no binding for nonlocal ‘a’ found

這是因為 nonlocal 表示外層變數,但是一旦外層變數是全域性變數,則只能用 global。如果將這段程式碼全部放到一個函式中,則可以使用 nonlocal :

def outer_fun():
    a = 1
    def fun():
        nonlocal  a # a為外層變數
        print(a) # 輸出1
        a = 2
    fun()
    print(a) #輸出2
outer_fun()

同理,此時如果將 nonlocal 改為 global,也是會報錯的:

builtins.NameError: name ‘a’ is not defined

注意:可以發現,之前的將 global 換為 nonlocal 的報錯,和這次的將 nonlocal 改為 global 的報錯,錯誤型別是不同的。前者是 nonlocal a 這一行報錯,後者是 print(a) 這一行報錯。也就是說,在使用 nonlocal a 之前,必須保證外層的確已經定義過 a 了,但是在 global a 的時候,可以允許全域性變數中還沒有定義過a,可以留在後面定義。比如將 print(a) 之前加上對a的定義,便不會出錯:

def outer_fun():
    a = 1
    def fun():
        global  a # a為全域性變數,與上面等於1的 a 沒有關係
        a = 3 # 定義全域性變數
        print(a) # 輸出3
        a = 2
    fun()
    print(a) #輸出1,區域性變數
outer_fun()
print(a) # 輸出2,全域性變數

4、只要在一個作用域中有 global 或者 nonlocal 命令,則不管這個命令在哪個位置,在整個作用域的開始到結尾都是有效的。
如將剛才例子的 global a 和 a = 3 互換位置,結果是一模一樣的:

def outer_fun():
    a = 1
    def fun():
        a = 3 # 此時python已經知道a是全域性變量了,這句話相當於定義一個全域性變數a,值為3
        global  a # a為全域性變數
        print(a) # 輸出3
        a = 2
    fun()
    print(a) #輸出1,區域性變數
outer_fun()
print(a) # 輸出2,全域性變數

如果我們註釋掉 global a 這一句,那麼 a = 3 就是新建了一個區域性變數,其實相當於例子2了。

def outer_fun():
    a = 1
    def fun():
        a = 3
        #global  a
        print(a) # 輸出3
        a = 2
    fun()
    print(a) #輸出1,fun()裡面的a已經釋放掉了
outer_fun()
#print(a) # 這一句也要註釋掉,不然會報錯,因為已經沒有全域性變數a了

也就是說,python不允許同一個區域性作用域中的同一個變數名有多種角色,即不允許在 global a 這句話之前是區域性變數、之後是全域性變數。所以,儘管python是解釋性語言、只有在執行時才會發現型別錯誤,但是並不代表後面的程式碼不會影響到前面的程式碼結果。