1. 程式人生 > >Python學習之變數的作用域

Python學習之變數的作用域

學習地址:http://www.jianshu.com/p/17a9d8584530

1、變數作用域LEGB

1.1變數的作用域

在Python程式中建立、改變、查詢變數名時,都是在一個儲存變數名的空間中進行,我們稱之為名稱空間,也被稱之為作用域。python的作用域是靜態的,在原始碼中變數名被賦值的位置決定了該變數能被訪問的範圍。即Python變數的作用域由變數所在原始碼中的位置決定。

1.2高階語言對資料型別的使用過程

一般的高階語言在使用變數時,都會有下面4個過程。當然在不同的語言中也會有著區別。

  1. 宣告變數:讓編輯器知道有這一個變數的存在
  2. 定義變數:為不同資料型別的變數分配記憶體空間
  3. 初始化:賦值,填充分配好的記憶體空間
  4. 引用:通過引用物件(變數名)來呼叫記憶體物件(記憶體資料)

1.3作用域的產生

就作用域而言,Python與C有著很大的區別,在Python中並不是所有的語句塊中都會產生作用域。只有當變數在Module(模組)、Class(類)、def(函式)中定義的時候,才會有作用域的概念。看下面的程式碼:

#!/usr/bin/env python
def func():
    variable = 100
    print variable
print variable

程式碼的輸出為:

NameError: name 'variable' is not defined

在作用域中定義的變數,一般只在作用域中有效。 需要注意的是:在if-elif-else、for-else、while、try-except\try-finally等關鍵字的語句塊中並不會產成作用域。看下面的程式碼:

if True:
    variable = 100
    print (variable)
print ("******")
print (variable)

程式碼的輸出為:

100
******
100

所以,可以看到,雖然是在if語句中定義的variable變數,但是在if語句外部仍然能夠使用。

1.4作用域的型別:

在Python中,使用一個變數時並不嚴格要求需要預先宣告它,但是在真正使用它之前,它必須被繫結到某個記憶體物件(被定義、賦值);這種變數名的繫結將在當前作用域中引入新的變數,同時遮蔽外層作用域中的同名變數。

L(local)區域性作用域

區域性變數:包含在def關鍵字定義的語句塊中,即在函式中定義的變數。每當函式被呼叫時都會建立一個新的區域性作用域。Python中也有遞迴,即自己呼叫自己,每次呼叫都會建立一個新的區域性名稱空間。在函式內部的變數宣告,除非特別的宣告為全域性變數,否則均預設為區域性變數。有些情況需要在函式內部定義全域性變數,這時可以使用global關鍵字來宣告變數的作用域為全域性。區域性變數域就像一個 棧,僅僅是暫時的存在,依賴建立該區域性作用域的函式是否處於活動的狀態。所以,一般建議儘量少定義全域性變數,因為全域性變數在模組檔案執行的過程中會一直存在,佔用記憶體空間。 注意:如果需要在函式內部對全域性變數賦值,需要在函式內部通過global語句宣告該變數為全域性變數。

E(enclosing)巢狀作用域

E也包含在def關鍵字中,E和L是相對的,E相對於更上層的函式而言也是L。與L的區別在於,對一個函式而言,L是定義在此函式內部的區域性作用域,而E是定義在此函式的上一層父級函式的區域性作用域。主要是為了實現Python的閉包,而增加的實現。

G(global)全域性作用域

即在模組層次中定義的變數,每一個模組都是一個全域性作用域。也就是說,在模組檔案頂層宣告的變數具有全域性作用域,從外部開來,模組的全域性變數就是一個模組物件的屬性。 注意:全域性作用域的作用範圍僅限於單個模組檔案內

B(built-in)內建作用域

系統內固定模組裡定義的變數,如預定義在builtin 模組內的變數。

1.5變數名解析LEGB法則

搜尋變數名的優先順序:區域性作用域 > 巢狀作用域 > 全域性作用域 > 內建作用域LEGB法則: 當在函式中使用未確定的變數名時,Python會按照優先順序依次搜尋4個作用域,以此來確定該變數名的意義。首先搜尋區域性作用域(L),之後是上一層巢狀結構中def或lambda函式的巢狀作用域(E),之後是全域性作用域(G),最後是內建作用域(B)。按這個查詢原則,在第一處找到的地方停止。如果沒有找到,則會出發NameError錯誤。

幾個例項:

1.

def func():
    variable = 300
    print variable

variable = 100
func() #300
print variable  #100

2.

variable = 300

def test_scopt():

  print variable #variable是test_scopt()的區域性變數,但是在列印時並沒有繫結記憶體物件。

  variable = 200 #因為這裡,所以variable就變為了區域性變數

test_scopt()

print variable

上面的例子會報出錯誤,因為在執行程式時的預編譯能夠在test_scopt()中找到區域性變數variable(對variable進行了賦值)。在區域性作用域找到了變數名,所以不會升級到巢狀作用域去尋找。但是在使用print語句將變數variable列印時,區域性變數variable並有沒繫結到一個記憶體物件(沒有定義和初始化,即沒有賦值)。本質上還是Python呼叫變數時遵循的LEGB法則和Python解析器的編譯原理,決定了這個錯誤的發生。所以,在呼叫一個變數之前,需要為該變數賦值(繫結一個記憶體物件)。 注意:為什麼在這個例子中觸發的錯誤是UnboundLocalError而不是NameError:name ‘variable’ is not defined。因為變數variable不在全域性作用域。Python中的模組程式碼在執行之前,並不會經過預編譯,但是模組內的函式體程式碼在執行前會經過預編譯,因此不管變數名的繫結發生在作用域的那個位置,都能被編譯器知道。Python雖然是一個靜態作用域語言,但變數名查詢是動態發生的,直到在程式執行時,才會發現作用域方面的問題