理解Python中的閉包

2021-09-01 19:39:53 字數 4881 閱讀 1975

理解python中的閉包

1.定義

閉包是函式式程式設計的乙個重要的語法結構,函式式程式設計是一種程式設計正規化 (而面向過程程式設計和物件導向程式設計也都是程式設計正規化)。在面向過程程式設計中,我們見到過函式(function);在物件導向程式設計中,我們見過物件(object)。函式和物件的根本目的是以某種邏輯方式組織**,並提高**的可重複使用性(reusability)。閉包也是一種組織**的結構,它同樣提高了**的可重複使用性。

不同程式語言實現閉包的方式是不同的,python中閉包從表現形式上看,如果在乙個內部函式裡,對在外部作用域(但不是在全域性作用域)的變數進行引用,那麼內部函式就被認為是閉包(closure)。

舉個例子:

def outer(x):

def inner(y):

return x + y

return inner

12345

結合這段簡單的**和定義來說明閉包:

inner(y)就是這個內部函式,對在外部作用域(但不是在全域性作用域)的變數進行引用:x就是被引用的變數,x在外部作用域outer裡面,但不在全域性作用域裡,則這個內部函式inner就是乙個閉包。

再稍微講究一點的解釋是,閉包=函式塊+定義函式時的環境,inner就是函式塊,x就是環境,當然這個環境可以有很多,不止乙個簡單的x。

在函式outer中定義了乙個inner函式,inner函式訪問外部函式outer的(引數)變數,並且把inner函式作為返回值返回給outer函式。

a = outer(2)

print('function:',a)

print('result:',a(3))123

上面的**中a就是乙個函式,**的執行結果為:

<'function:',>

<'result:',5>

從結果我們不難看出,a是函式inner而不是outer,這個有點繞,但是並不難理解,因為return回來的是inner函式。

print('a.func_name',a.func_name)1
輸出結果為:

<'a.func_name','inner'>
呼叫函式a,得到的結果是傳入引數的值相加。

上面的和這句是一樣的:print(『result:』,outer(2)(3))

2.使用閉包注意的地方

2.1閉包無法修改外部函式的區域性變數

如果innerfunc可以修改x的值的話,x的值前後會發生變化,但結果是:

('outer x before call inner:', 0)

('inner x: ', 1)

('outer x after call inner:', 0)

在innerfunc中x的值發生了改變,但是在outerfunc中x的值並未發生變化。

再來看乙個例子

2.2閉包無法直接訪問外部函式的區域性變數

def outer():

x = 5

def inner(): #上面一行的x相對inner函式來說是函式外的區域性變數(非全域性變數)

x *= x

return x

return inner

outer()()12345678

執行會報錯:

unboundlocalerror:local variable 'x' referenced before assignment

報錯:區域性變數在宣告之前引用,inner函式不能訪問到外部的區域性變數

解決的方法:

1.在python3之前沒有直接的解決方法,只能間接地通過容器型別來解決,因為容器型別不是存放在棧空間的,inner函式可以訪問到。

def outer():

x = [5]

def inner():

x[0] *= x[0]

return x[0]

return inner

print(outer()()) #2512345678

2.python3通過nonlocal關鍵字來解決,該語句顯式的指定a不是閉包的區域性變數。

def outer():

x = 5

def inner():

nonlocal x #把x宣告為非區域性變數

x *= x

return x

return inner

print(outer()())123456789

2.3python迴圈中不包含域的概念

還有乙個容易產生錯誤的事例也經常被人在介紹python閉包時提起,我一直都沒覺得這個錯誤和閉包有什麼太大的關係,但是它倒是的確是在python函式式程式設計是容易犯的乙個錯誤,我在這裡也不妨介紹一下。先看下面這段**

for i in range(3):  

print i

123

在程式裡面經常會出現這類的迴圈語句,python的問題就在於,當迴圈結束以後,迴圈體中的臨時變數i不會銷毀,而是繼續存在於執行環境中。還有乙個python的現象是,python的函式只有在執行時,才會去找函式體裡的變數的值。

flist =   

for i in range(3):

def foo(x): print x + i

for f in flist:

f(2)

123456

可能有些人認為這段**的執行結果應該是2,3,4.但是實際的結果是4,4,4。loop在python中是沒有域的概念的,flist在像列表中新增func的時候,並沒有儲存i的值,而是當執行f(2)的時候才去取,這時候迴圈已經結束,i的值是2,所以結果都是4。

解決方法也很簡單,改寫一下函式的定義就可以了。

for i in range(3):   

def foo(x,y=i): print x + y

1234

3.閉包的作用

說了這麼多,不免有人要問,那這個閉包在實際的開發中有什麼用呢?閉包主要是在函式式開發過程中使用。以下介紹兩種閉包主要的用途。

用途1:當閉包執行完後,仍然能夠保持住當前的執行環境。

比如說,如果你希望函式的每次執行結果,都是基於這個函式上次的執行結果。我以乙個類似棋盤遊戲的例子來說明。假設棋盤大小為50*50,左上角為座標系原點(0,0),我需要乙個函式,接收2個引數,分別為方向(direction),步長(step),該函式控制棋子的運動。棋子運動的新的座標除了依賴於方向和步長以外,當然還要根據原來所處的座標點,用閉包就可以保持住這個棋子原來所處的座標。

origin = [0, 0] 

legal_x = [0, 50]

legal_y = [0, 50]

def create(pos=origin):

def player(direction,step):

# 這裡應該首先判斷引數direction,step的合法性,比如direction不能斜著走,step不能為負等

# 然後還要對新生成的x,y座標的合法性進行判斷處理,這裡主要是想介紹閉包,就不詳細寫了。

new_x = pos[0] + direction[0]*step

new_y = pos[1] + direction[1]*step

pos[0] = new_x

pos[1] = new_y

#注意!此處不能寫成 pos = [new_x, new_y],因為引數變數不能被修改,而pos是容器類的解決方法

return pos

return player

player = create() # 建立棋子player,起點為原點

print player([1,0],10) # 向x軸正方向移動10步

print player([0,1],20) # 向y軸正方向移動20步

print player([-1,0],10) # 向x軸負方向移動10步 12345678910111213141516171819

輸出為:

[10, 0] 

[10, 20]

[0, 20] 123

用途2:閉包可以根據外部作用域的區域性變數來得到不同的結果

這有點像一種類似配置功能的作用,我們可以修改外部的變數,閉包根據這個變數展現出不同的功能。比如有時我們需要對某些檔案的特殊行進行分析,先要提取出這些特殊行。

def make_filter(keep):   

def the_filter(file_name):

file = open(file_name)

lines = file.readlines()

file.close()

filter_doc = [i for i in lines if keep in i]

return filter_doc

return the_filter 12345678

如果我們需要取得檔案」result.txt」中含有」pass」關鍵字的行,則可以這樣使用例子程式

filter = make_filter("pass") filter_result = filter("result.txt") 

12

以上兩種使用場景,用物件導向也是可以很簡單的實現的,但是在用python進行函式式程式設計時,閉包對資料的持久化以及按配置產生不同的功能,是很有幫助的。

python閉包怎麼理解 Python 閉包的理解

很多函式型的語言都有閉包這一概念,比如python的兄弟js。人們剛聽到閉包這一概念總會覺得它很晦澀,難以理解。其實不然,主要是他的名字起得太抽象了,讓人誤以為很難。下面舉乙個例子 coding utf 8 def foo nums 0 5 for i in range 5 nums i lambd...

python中閉包 python中的閉包

一 定義 如果在乙個內部函式裡,對在外部作用域 但不是在全域性作用域 的變數進行引用,那麼內部函式就被認為是閉包 closure 這個定義是相對直白的,閉包有三個條件 1.函式巢狀 2,內部函式呼叫外部函式的變數 3.返回內部函式 defa a defb b s a breturnsreturnb ...

python 閉包 Python中的閉包

一 什麼是閉包 在談之前,我們先來說說作用域,變數的作用域無非就兩種 全域性變數和區域性變數。函式內部可以直接讀取全域性變數,但是在函式外部無法讀取函式內部的區域性變數。出於種種原因,我們有時候需要獲取到函式內部的區域性變數。但是,正常情況下,這是辦不到的!只有通過變通的方法才能實現。於是就引入了閉...