您現在的位置是:首頁 > 武術

Python中令人頭疼的變數作用域問題,終於弄清楚了

  • 由 CDA資料分析師 發表于 武術
  • 2022-04-25
簡介我們修改上例中的inner函式為如下形式:definner(i1,i2=‘i2’):e = ‘enclose’g = ‘inner global’print(i1,i2,o1,o2,e,g,b)在巢狀函式內,重新定義

變數的作用域分為幾種

作者:大奎

整理:陽哥

Python中令人頭疼的變數作用域問題,終於弄清楚了

大家好,我是陽哥。

學習Python變數過程中,曾經為變數混亂的作用域問題頭疼不已,全域性變數、區域性變數、自由變數傻傻分不清,今天來跟大家分享 Python變數作用域 的知識點,文章內容由公眾號讀者 大奎 創作。

我們經常聽說Python函式訪問區域性變數、全域性變數;在定義裝飾器的時候,還會使用自由變數。這些不同的變數是如何賦值、初始化、查詢及修改的呢?各自的作用細則又是什麼樣的呢?本篇嘗試解答這個問題。

Python中的變數名可以指代變數、函式、類、物件等。一般來說,每個物件都有一個變數名

指向

,更準確說是

繫結

作用域的必要性

為啥變數要有作用域呢?

我們在Python裡遇到的內建、區域性、全域性及自由變數,就是說變數的作用域。

語言區分作用域,是為了複用變數名。引入作用域,相當於給變數劃分了各自的“隔離區”,在不同”隔離區“裡,查詢變數變得很容易。

正是因為有了作用域,我們在函式內才可以隨意使用變數名,而不擔心其與全域性變數、其他函式中的變數衝突——因為這兩個作用域是分割的。

BASIC語言只有全域性變數,你能想象嗎?你在一個函數里命名的迴圈變數i,很可能跟全域性變數衝突。寫起程式來,舉步維艱。且會導致很多修改、檢索問題,維護很困難。

Python變數定義的時間和空間

Python 有哪些作用域呢?

Python是動態型別語言,變數是在定義的時候賦值的。這句話的意思我們分以下幾個方面來理解:

a = 1 賦值時定義變數

from tools import cubie 匯入時定義變數 cubie

def fun():pass 定義函式,繫結變數fun

def fun(name=None):pass 定義變數name為函式fun的形式變數(也是區域性變數),同時定義函式,繫結便令fun

class Car:pass 定義類,繫結類名Car

以上,我們弄清了變數定義的時刻,下面來看變數的作用域,也就是變數的活動空間怎麼規定出來的。

變數作用域取決於其

定義位置

定義在函式內部的變數、定義在函式宣告中的形式引數,視為區域性變數。

定義在 。py 檔案內的,且函式、類之外的變數,視為全域性變數。

定義在函式中,巢狀函式外,且被巢狀函式引用的變數,視為自由變數。

定義在builtin中的變數,視為內建變數。

面對如此複雜的四種變數作用域,用一個例子來說明它們的訪問規則。

LEGB規則

四個作用域遵循LEGB規則,讓我們用一個例子來說明。

import builtinsbuiltins。b = ‘builtins’g = ‘global’def outer(o1,o2=‘o2’):e = ‘enclose’def inner(i1,i2=‘i2’):print(i1,i2,o1,o2,e,g,b) returninnerfun = outer(‘o1’)fun(‘i1’)

其輸出為 i1 i2 o1 o2 enclose global builtins

可見,在outer函式的巢狀函式inner中的輸出語句 print(i1,i2,o1,o2,e,g,b) 是本程式的重點。其具體執行情況如下:

print i1和i2,毫無疑問的區域性變數。

print o1和o2,本地作用域沒有,向上查詢到outer函式形參。形參也為區域性變數,所以該變數實際定義在outer函式內,inner這個內嵌函式外,而inner內部引用了這個變數,所以視為自由變數。

print e,本地作用域沒有,類似上例,視為自由變數。

print g,本地作用域沒有,自由變數作用域(閉包)沒有,一直上溯到全域性作用局找到。

print b,本地作用域沒有,自由變數作用域(閉包)沒有,全域性作用局沒有,一致上溯到內建變數空間找到。

至此,LEGB規則呼之欲出:在本地空間尋找不到的變數,逐級向上級尋找。這裡的

LEGB分別指代Local,Enclose,Global和Builtin。

在函式中

讀取

賦值

全域性變數,在內嵌函式中

讀取

賦值

自由變數,會有一些不同的地方。

nonlocal 和 global

對變數名的賦值和引用,是兩種不同的情況:

賦值:建立一個變數或者修改。

引用:檢索其值。

以上兩者的差別,會導致我們在函式中:

賦值一個

全域性變數:等於建立一個區域性變數。

自由變數:等於建立一個區域性變數。

引用:正常檢索其值。

我們修改上例中的inner函式為如下形式:

definner(i1,i2=‘i2’): e = ‘enclose’ g = ‘inner global’ print(i1,i2,o1,o2,e,g,b)

在巢狀函式內,重新定義了g變數,其他語言一般理解這是重新賦值全域性變數。但是我們看上條規則:

在函式中,賦值一個全域性變數時,等於建立一個區域性變數

。就是說此時的g已經是區域性變量了——在程式最後的 print(g) 語句輸出 global,而不是修改後的 inner global 也驗證了以上規則。

完整程式碼如下:

import builtinsbuiltins。b = ‘builtins’g = ‘global’def outer(o1,o2=‘o2’): e = ‘enclose’ g = ‘inner global’ def inner(i1,i2=‘i2’): print(i1,i2,o1,o2,e,g,b) returninnerfun = outer(‘o1’)fun(‘i1’)print(g)

輸出結果如下:

i1 i2 o1 o2 enclose inner global builtinsglobal

不重新賦值,只是使用全域性變數和自由變數,則沒有問題。

自由變數也是類似的情況。

為了解決區域性作用域中賦值全域性變數和自由變數導致的變成區域性變數問題,Python引入關鍵字 global 和 nonlocal 。

definner(i1,i2=‘i2’):global g nonlocal e g = ‘inner global’ e = ‘inner enclose’

此時的賦值,則分別是對全域性變數和自由變數的操作,而非新建區域性變數。

完整程式碼如下:

importbuiltinsbuiltins。b = ‘builtins’g = ‘global’defouter(o1,o2=‘o2’): e = ‘enclose’definner(i1,i2=‘i2’):global g nonlocal e g = ‘inner global’ e = ‘inner enclose’ print(i1,i2,o1,o2,e,g,b) return inner fun = outer(‘o1’) fun(‘i1’)print(g)

輸出結果如下:

i1 i2 o1 o2 inner enclose inner global builtinsinner global

總結

Python的作用域分為四種,分別是區域性、全域性、自由和內建;

定義變數的位置決定了變數的作用域;

作用域的查詢遵守LEGB規則;

為了在區域性作用域中修改全域性變數和自由變數,引入了 global 關鍵字和 nonlocal 關鍵字。

Top