Python基礎知識:什麼是非區域性語句?

發表於2016-03-14

有同學曾在微信中問小編什麼是非區域性語句(nonlocal statement),本文就是對此的回答,希望沒有發的太晚。
非區域性語句是Python 3.x中新引入的特性,可以讓你給外層但非全域性作用域中的變數賦值。官方文件中的說法是,非區域性語句可以讓所列的識別符號(identifier)指向最近的巢狀作用域(enclosing scope)中已經繫結過的變數,全域性變數除外。

如果沒有非區域性語句

一般來說,巢狀函式對於其外層作用域中的變數是有訪問許可權的。

我們在outside函式中宣告瞭msg變數,並賦值為“Outside!”。然後,在inside函式中列印msg的值。結果證明,inside成功獲得了外層作用域中msg的值。

但是如果我們想給外層作用域中的變數賦值時,是不是按照平常的賦值操作就可以修改它的值呢?

inside函式中,我們想給msg變數賦值為”Inside!”。執行outside時,inside函式中msg的值為”Inside!”,但是在outside函式中卻保留了原先的值!

之所以出現這個情況,是因為在inside函式中,Python實際上並沒有為之前已經建立的msg變數賦值,而是在inside函式的區域性作用域(local scope)中建立了一個名叫msg的新變數,但是這樣就和外層作用域(outer scope)中的變數重名了。

這說明,巢狀函式對外層作用域中的變數其實只有只讀訪問許可權。如果我們在這個示例中的inside函式的頂部再加一個print(msg)語句,那麼就會出現UnboundLocalError: local variable 'msg' referenced before assignment這個錯誤。

非區域性語句的引入,就是要儘量減少這種變數名衝突情況的出現,同時也讓巢狀函式更加方便的操作外層函式中的變數。更加詳細的原因,請看參考資料部分的PEP-3104。

使用非區域性語句之後

接下來,我們引入nonlocal語句。

現在,我們在inside函式的頂部新增了nonlocal msg語句。這個語句的作用,就是告訴Python直譯器在碰到為msg賦值的語句時,應該向外層作用域的變數賦值,而不是宣告一個重名的新變數。這樣,兩個函式的列印結果就一致了。

nonlocal的用法和global非常類似,只是前者針對的是外層函式作用域的變數,後者針對的則是全域性作用域的變數。

什麼時候該使用非區域性語句

有時候,你可能會疑惑什麼時候才應該使用nonlocal。以下面的函式為例:

你可能會想,因為沒有使用nonlocalinside函式中往字典d中插入的"inside": 2鍵值對(key-value pair)不會體現在outside函式中。你這麼想挺合理,但卻是錯的。因為字典插入並不是賦值操作,而是方法呼叫(method call)。事實上,往字典中插入一個鍵值對相當於呼叫字典物件中的__setitem__方法。

所以,這個示例中我們可以不使用nonlocal,就能直接操作外層作用域中的變數。

小結

其實在許多Python程式中,很少用到非區域性語句。但是,有了這種語句之後,我們就可以減少不同作用域之間變數名的衝突。非區域性語句,也讓我們更加容易地訪問、操作外層作用域中的變數。不過,這在一定程度上也讓語法變得更加複雜。

有關變數、語句等術語的基礎知識,還可以參考《Think Python 2e》的第二章:量、表示式和語句

參考資料

  1. Simple Statements
  2. PEP-3104
  3. Global-Nonlocal
  4. PyTips

相關文章