1. 程式人生 > 實用技巧 >一篇文章學會 Python 中閉包與變數作用域

一篇文章學會 Python 中閉包與變數作用域

1. 作用域

Python的作用域可以分為四種:

  • L (Local) 區域性作用域
  • E (Enclosing) 閉包函式外的函式中
  • G (Global) 全域性作用域
  • B (Built-in) 內建作用域

變數/函式 的查詢順序:
L –> E –> G –>B

意思是,在區域性找不到的,便去區域性外的區域性作用域找(例如 閉包),再找不到的就去全域性作業域裡找,再找不到就去內建作業域中找。

會影響 變數/函式 作用範圍的有

  • 函式:def 或 lambda
  • 類:class
  • 關鍵字:global noglobal
  • 檔案:*py
  • 推導式:[],{},()等,僅限Py3.x中,Py2.x會出現變數洩露。

1、賦值在前,引用在後

# ------同作用域內------
name = "MING"
print(name)

# ------不同作用域內------
name = "MING"
def main():
    print(name)

2、引用在前,賦值在後(同一作用域內)

print(name)
name = "MING"

# UnboundLocalError: local variable 'name' referenced before assignment

3、賦值在低層,引用在高層

# L -> E -> G -> B
# 從左到右,由低層到高層
def main():
    name = "MING"

print(name)
# NameError: name 'name' is not defined

2. 閉包

在一個外函式中定義了一個內函式,內函式裡運用了外函式的臨時變數,並且外函式的返回值是內函式的引用。這樣就構成了一個閉包。其實裝飾函式,很多都是閉包。

好像並不難理解,為什麼初學者會覺得閉包難以理解呢?

我解釋一下,你就明白了。

一般情況下,在我們認知當中,如果一個函式結束,函式的內部所有東西都會釋放掉,還給記憶體,區域性變數都會消失。但是閉包是一種特殊情況,如果外函式在結束的時候發現有自己的臨時變數將來會在內部函式中用到,就把這個臨時變數繫結給了內部函式,然後自己再結束。

你可以看下面這段程式碼,就構成了閉包。在內函式裡可以引用外函式的變數。

def deco():
    name = "MING"
    def wrapper():
        print(name)
    return wrapper

deco()()
# 輸出:MING

3. 改變作用域

變數的作用域,與其定義(或賦值)的位置有關,但不是絕對相關。
因為我們可以在某種程度上去改變向上的作用範圍。

  • 關鍵字:global
    將 區域性變數 變為全域性變數

  • 關鍵字:nonlocal
    可以在閉包函式中,引用並使用閉包外部函式的變數(非全域性的噢)

global好理解,這裡只講下nonlocal。

先來看個例子

def deco():
    age = 10
    def wrapper():
        age += 1
    return wrapper

deco()()

執行一下,會報錯。

# UnboundLocalError: local variable 'age' referenced before assignment

但是這樣就OK

def deco():
    age = 10
    def wrapper():
        nonlocal age
        age += 1
    return wrapper

deco()()
# 輸出:11

其實,你如果不使用 +=-=等一類的操作,不加nonlocal也沒有關係。這就展示了閉包的特性。

def deco():
    age = 10
    def wrapper():
        print(age)
    return wrapper

deco()()
# 輸出:10

4. 變數集合

在Python中,有兩個內建函式,你可能用不到,但是需要知道

  • globals() :以dict的方式儲存所有全域性變數
  • locals():以dict的方式儲存所有區域性變數

globals()

def foo():
    print("I am a func")

def bar():
    foo="I am a string"
    foo_dup = globals().get("foo")
    foo_dup()

bar()
# 輸出
# I am a func

locals()

other = "test"

def foobar():
    name = "MING"
    gender = "male"
    for key,value in locals().items():
        print(key, "=", value)

foobar()
# 輸出
# name = MING
# gender = male

文末福利

本人原創的 《PyCharm 中文指南》一書前段時間一經發布,就火爆了整個 Python 圈,釋出僅一天的時間,下載量就突破了 1000 ,並且在當天就在 Github 上就收穫了數百的 star,截至目前,下載量已經破萬。

這本書一共將近 200 頁內含大量的圖解製作之精良,值得每個 Python 工程師 人手一份。

為方便你下載,我將這本書上傳到 百度網盤上了,你可以自行獲取。

連結:https://pan.baidu.com/s/1-NzATHFtaTV1MQzek70iUQ

密碼:mft3