變量作用域是指我們在程序中命名的變量在多大範圍能夠訪問到它。在函數內部聲明的變量,在函數外部是否能夠訪問?在模塊中聲明的變量,在函數內部是否能夠訪問?這些都是變量作用域要解決的問題。下面是一段有關變量作用域範圍的代碼。
上面的代碼是求圓的面積。在模塊頭部聲明了變量π,變量π被定義的area函數在內部使用,area函數求出圓的面積並輸出結果。print語句在這段代碼中使用了兩次:第一次是在area函數內部使用,輸入參數是在area函數內部聲明的變量s;第二次是在模塊中使用,輸入參數是在area函數內部聲明的變量s。下圖是上面代碼的執行結果。
從上圖執行結果可以看出。第一條print語句被正確執行,輸出了圓的面積。第二條print語句在執行過程中報錯,錯誤信息是「NameError: name 's' is not defined」,大體意思是「名稱錯誤:名稱『s』沒有被定義」。從給出的錯誤信息可以得出下面的結論:
在函數內部聲明的變量不能在函數外部訪問,函數內部聲明的變量為局部變量,其作用域僅限於函數內部。
有局部變量就有全局變量,上面代碼中的π就是全局變量。這裡的全局變量是指在模塊範圍內的全局變量,其作用域是整個模塊。全局變量可以在模塊內的函數內部使用,但需要遵循先聲明後使用的原則。
使用global關鍵字可以提升函數內部的局部變量為全局變量,當使用global關鍵字修飾變量時,該變量被提升為全局變量。
在上面的代碼中。函數area內部聲明的s使用了global關鍵字,局部變量s被提升為全局變量,因此在函數的外部也可以使用變量s。下圖是上面代碼的執行結果。
從執行結果可以看出。代碼中的第二條print語句被正確執行,輸出了圓的面積,其輸入的參數就是在area函數內部聲明的變量s。
在上面的探討中,我們了解了局部變量和全局變量的作用域,也知道了使用global關鍵字可以把函數內部的局部變量提升為全局變量。不過還有個問題沒有弄明白,當全局變量的名稱和函數體內局部變量的名稱相同時,哪個變量會被使用呢?
上面的代碼在area函數內部又聲明了一個變量π,這個變量π是局部變量,其精度要比全局變量π高。那麼問題來了,在程序執行過程中,計算圓的面積是用局部變量的π?還是用全局變量的π呢?下圖是上面代碼的執行結果。
執行結果給出了答案,當模塊內全局變量的名稱和函數體內局部變量的名稱相同時,在函數體內聲明的局部變量將覆蓋與其名稱相同的全局變量。
Python語言是支持函數嵌套的,即在函數體內部可以嵌套定義子函數。那麼嵌套的子函數是否可以在函數外部調用呢?它的作用域又是什麼範圍呢?
上面的代碼定義了foo函數,在foo函數內部又嵌套定義了bar子函數。Bar子函數使用了foo函數聲明的局部變量m,並輸出m與n的和。foo函數的最後一條語句調用了bar函數。需要記住,函數必須被調用後才執行函數體內代碼。
下圖是上面代碼的執行結果。
從執行結果可以看出。foo函數被正確執行,並輸出了正確的結果數字7。因此在嵌套函數中,子函數內部可以訪問在父函數聲明的變量。對子函數來說,父函數聲明的變量在整個函數體內就是全局變量。代碼中的最後一條語句是調用foo函數內部嵌套的子函數bar,在執行到這條語句時程序報錯,顯然在函數體內嵌套的函數是不能被外部調用的,其作用域僅限於函數體內部。
本文主要討論了全局變量和局部變量的作用域,也討論了嵌套函數的作用域,並了解了局部變量或嵌套函數僅限於在函數體內使用。如果要將局部變量或嵌套函數引入到全局環境中使用,這就需要用到閉包的概念了,閉包將放在後面介紹。