關(guān)于Python中的閉包詳解
1、閉包的概念
請大家跟我理解一下,如果在一個函數(shù)的內(nèi)部定義了另一個函數(shù),外部的我們叫他外函數(shù),內(nèi)部的我們叫他內(nèi)函數(shù)。閉包: 在一個外函數(shù)中定義了一個內(nèi)函數(shù),內(nèi)函數(shù)里運用了外函數(shù)的臨時變量,并且外函數(shù)的返回值是內(nèi)函數(shù)的引用。這樣就構(gòu)成了一個閉包。一般情況下,在我們認知當中,如果一個函數(shù)結(jié)束,函數(shù)的內(nèi)部所有東西都會釋放掉,還給內(nèi)存,局部變量都會消失。但是閉包是一種特殊情況,如果外函數(shù)在結(jié)束的時候發(fā)現(xiàn)有自己的臨時變量將來會在內(nèi)部函數(shù)中用到,就把這個臨時變量綁定給了內(nèi)部函數(shù),然后自己再結(jié)束。
2、實現(xiàn)一個閉包
# 將函數(shù)作為返回值返回,也是一種高階函數(shù)
# 這種高階函數(shù)我們也稱為叫做閉包,通過閉包可以創(chuàng)建一些只有當前函數(shù)能訪問的變量
# 可以將一些私有的數(shù)據(jù)藏到的閉包中
def outer():
a = 10
# 函數(shù)內(nèi)部再定義一個函數(shù)
def inner():
print('我是outer', a)
# 將內(nèi)部函數(shù) inner作為返回值返回
return inner
# r是一個函數(shù)對象,是調(diào)用fn()后返回的函數(shù)對象
# 這個函數(shù)實在fn()內(nèi)部定義,并不是全局函數(shù)
# 所以這個函數(shù)總是能訪問到fn()函數(shù)內(nèi)的變量
# 外函數(shù)返回了內(nèi)函數(shù)的引用
fn = outer()
# r()相當于調(diào)用了inner()函數(shù)
print("outer引用地址:", outer)
print("inner引用地址:", fn)
fn()
"""
輸出結(jié)果:
outer引用地址: <function outer at 0x0000000002BB5948>
inner引用地址: <function outer.<locals>.inner at 0x0000000002BB58B8>
我是outer 10
"""說明上述代碼:
對于閉包,在外函數(shù)outer中 最后return inner,我們在調(diào)用外函數(shù) fn = outer() 的時候,outer函數(shù)返回了inner函數(shù)對象,inner函數(shù)對象是一個函數(shù)的引用,這個引用被存入了fn對象中。所以接下來我們再進行fn() 的時候,相當于運行了inner函數(shù)。
提示:
一個函數(shù),如果函數(shù)名后緊跟一對括號,相當于調(diào)用這個函數(shù)。如果不跟括號,相當于只是一個函數(shù)的名字,里面存了函數(shù)所在位置的引用。
3、在閉包中外函數(shù)把臨時變量綁定給內(nèi)函數(shù)
按照我們正常的認知,一個函數(shù)結(jié)束的時候,會把自己的臨時變量都釋放還給內(nèi)存,之后變量都不存在了。一般情況下,確實是這樣的。但是閉包是一個特別的情況。外部函數(shù)發(fā)現(xiàn),自己的臨時變量會在將來的內(nèi)部函數(shù)中用到,自己在結(jié)束的時候,返回內(nèi)函數(shù)的同時,會把外函數(shù)的臨時變量送給內(nèi)函數(shù)綁定在一起。所以外函數(shù)已經(jīng)結(jié)束了,調(diào)用內(nèi)函數(shù)的時候仍然能夠使用外函數(shù)的臨時變量。
在我編寫的實例中,我兩次調(diào)用外部函數(shù)outer,分別傳入的值是10和20。內(nèi)部函數(shù)只定義了一次,我們發(fā)現(xiàn)調(diào)用的時候,內(nèi)部函數(shù)是能識別外函數(shù)的臨時變量是不一樣的。
Python中一切都是對象,雖然函數(shù)我們只定義了一次,但是外函數(shù)在運行的時候,實際上是按照里面代碼執(zhí)行的,外函數(shù)里創(chuàng)建了一個函數(shù),我們每次調(diào)用外函數(shù),它都創(chuàng)建一個內(nèi)函數(shù),雖然代碼一樣,但是卻創(chuàng)建了不同的對象,并且把每次傳入的臨時變量數(shù)值綁定給內(nèi)函數(shù),再把內(nèi)函數(shù)引用返回。
所以我們每次調(diào)用外函數(shù),都返回不同的實例對象的引用,他們的功能是一樣的,但是它們實際上不是同一個函數(shù)對象。
下面示例進行演示:
def outer(num):
a = num
# 函數(shù)內(nèi)部再定義一個函數(shù)
def inner():
print('我是outer', a)
# 將內(nèi)部函數(shù) inner作為返回值返回
return inner
fn1 = outer(10)
fn2 = outer(20)
print("inner引用地址:", fn1)
fn1()
print("inner引用地址:", fn2)
fn2()
"""
輸出結(jié)果:
inner引用地址: <function outer.<locals>.inner at 0x00000000026B58B8>
我是outer 10
inner引用地址: <function outer.<locals>.inner at 0x00000000026B5828>
我是outer 20
"""
# 注意兩個inner的地址不一樣,一個是8B8,一個是828。4、閉包中內(nèi)函數(shù)修改外函數(shù)局部變量
在基本的Python語法當中,一個函數(shù)可以隨意讀取全局數(shù)據(jù),但是要修改全局數(shù)據(jù)的時候有兩種方法
global聲明全局變量。全局變量是可變類型數(shù)據(jù)的時候可以修改。
在閉包內(nèi)函數(shù)也是類似的情況。在內(nèi)函數(shù)中想修改閉包變量(外函數(shù)綁定給內(nèi)函數(shù)的局部變量)的時候,在Python3中,可以用nonlocal關(guān)鍵字聲明一個變量, 表示這個變量不是局部變量空間的變量,需要向上一層變量空間找這個變量。
示例:
def outer(num):
a = num
b = 10 # a和b都是閉包變量
print("原始a值為", a)
# inner內(nèi)函數(shù)
def inner():
# 內(nèi)函數(shù)中想修改閉包變量
# nonlocal關(guān)鍵字聲明變量
nonlocal a
a += b
print('我是outer的a', a)
# 將內(nèi)部函數(shù) inner作為返回值返回
return inner
fn1 = outer(10)
fn1()
"""
輸出結(jié)果:
原始a值為 10
我是outer的a 20
"""在內(nèi)函數(shù)中,對閉包變量進行了修改,打印出來的結(jié)果也確實是修改之后的結(jié)果。
5、注意:
還有一點需要注意,閉包變量實際上只有一份,每次調(diào)用一份閉包變量。(這個在Python實現(xiàn)的單利模式下來解釋更多)
def outer(num):
a = num
b = 10 # a和b都是閉包變量
print("原始a值為", a)
# inner內(nèi)函數(shù)
def inner():
# 內(nèi)函數(shù)中想修改閉包變量
# nonlocal關(guān)鍵字聲明變量
nonlocal a
a += b
print('我是outer的a', a)
# 將內(nèi)部函數(shù) inner作為返回值返回
return inner
fn1 = outer(10)
fn1()
fn1()
fn1()
"""
輸出結(jié)果:
原始a值為 10
我是outer的a 20
我是outer的a 30
我是outer的a 40
"""可以看到第二次第二次調(diào)用fn1()方法,a的值有增加了10。
6、練習:
# 求多個數(shù)的平均值
# nums = [50,30,20,10,77]
# sum()是一個求和函數(shù)
# sum()用來求一個列表中所有元素的和
# print(sum(nums)/len(nums))
# 結(jié)果:37.4
# 形成閉包的要件
# ① 函數(shù)嵌套
# ② 將內(nèi)部函數(shù)作為返回值返回
# ③ 內(nèi)部函數(shù)必須要使用到外部函數(shù)的變量
def make_averager():
# 創(chuàng)建一個列表,用來保存數(shù)值
nums = []
# 創(chuàng)建一個函數(shù),用來計算平均值
def averager(n) :
# 將n添加到列表中
nums.append(n)
# 求平均值
return sum(nums)/len(nums)
return averager
# 創(chuàng)建對象,現(xiàn)在就是獲得了內(nèi)函數(shù)對象的引用
averager = make_averager()
# 調(diào)用內(nèi)涵書對象
# 這里注意,雖然是調(diào)用外函數(shù)創(chuàng)建的對象,
# 但是獲得的是內(nèi)函數(shù)對象的引用,內(nèi)函數(shù)是有形參的,
# 所以averager對象相當于是內(nèi)函數(shù)對象。
# 所以調(diào)用內(nèi)函數(shù)就要傳遞形參。
print(averager(10))
print(averager(20))
print(averager(30))
print(averager(40))總結(jié)
到此這篇關(guān)于關(guān)于Python中的閉包詳解的文章就介紹到這了,更多相關(guān)Python閉包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python基礎(chǔ)之包的導(dǎo)入和__init__.py的介紹
這篇文章主要介紹了python基礎(chǔ)之包的導(dǎo)入和__init__.py的相關(guān)資料,需要的朋友可以參考下2018-01-01
基于Python輕松實現(xiàn)PDF轉(zhuǎn)圖片
PDF文件是我們在日常工作和學習中常用的文檔格式之一,但你知道嗎,你可以將PDF文件轉(zhuǎn)換為圖像,讓文檔變得更加生動有趣,下面我們就來看看具體的實現(xiàn)方法吧2023-08-08
解決TensorFlow GPU版出現(xiàn)OOM錯誤的問題
今天小編就為大家分享一篇解決TensorFlow GPU版出現(xiàn)OOM錯誤的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-02-02
python實現(xiàn)調(diào)用其他python腳本的方法
python實現(xiàn)調(diào)用其他python腳本的方法,是一個比較實用的技巧,需要的朋友可以參考下2014-10-10

