深入解析python返回函數(shù)和匿名函數(shù)
此文章繼續(xù)上篇高階函數(shù),地址:python函數(shù)式編程以及高階函數(shù)
一、返回函數(shù)
高階函數(shù)的特性,除了可以接受函數(shù)
作為參數(shù)之外,高階函數(shù)還可以返回函數(shù)
下面來看幾個(gè)案例:
1、定義一個(gè)求和的函數(shù),可以這樣寫 # -*- coding: utf-8 -*- def test_1(*args): i = 0 for n in args: i = i + n return i print(test_1(10,20,30)) #輸出 60 但是如果不需要立即求和,而是需要在后面的代碼中再進(jìn)行計(jì)算改怎么辦,當(dāng)出現(xiàn)這種情況時(shí),就可以不返回求和的結(jié)果,而是返回求和的參數(shù),修改后可以這樣寫: # -*- coding: utf-8 -*- def test_1(*args): def test_sum(): i = 0 for n in args: i = i + n return i return test_sum f = test_1(10,20,30) print(f) print(f()) #輸出 <function test_1.<locals>.test_sum at 0x0000020483CAE7A0> 60 可以看出,當(dāng)把函數(shù)的結(jié)果賦值給f時(shí),直接輸出f,返回的是函數(shù),只有在調(diào)用 f( ) 時(shí),才會(huì)返回結(jié)果
看過上面的案例,還可以發(fā)現(xiàn)一件事,就是函數(shù)內(nèi)部定義函數(shù)是可以直接調(diào)用最外層函數(shù)的參數(shù)的,而在函數(shù)內(nèi)部定義的函數(shù),這種函數(shù)又叫內(nèi)部函數(shù)
,最外層的函數(shù)叫外部函數(shù)
1、閉包
內(nèi)部函數(shù)可以引用外部函數(shù)的參數(shù)和局部變量
,當(dāng)外部函數(shù)返回內(nèi)部函數(shù)時(shí),相關(guān)的參數(shù)和變量都保存在返回的內(nèi)部函數(shù)中,這種程序結(jié)構(gòu)又稱為“閉包(Closure)”
上面的內(nèi)部函數(shù)test_sum就引用了局部函數(shù)args
需要注意的是每次調(diào)用外部函數(shù)test_1()時(shí),每次調(diào)用都會(huì)生成一個(gè)新的函數(shù),即便傳入相同的參數(shù):
# -*- coding: utf-8 -*- def test_1(*args): def test_sum(): i = 0 for n in args: i = i + n return i return test_sum f1 = test_1(10,20,30) f2 = test_1(10,20,30) print(f1) print(f1()) print(f2) print(f2()) if f1 == f2 : print("yes") else: print("error") #輸出 <function test_1.<locals>.test_sum at 0x000001F27E2AE7A0> 60 <function test_1.<locals>.test_sum at 0x000001F27E2AE8C0> 60 error 可以看到,就算參數(shù)相同、返回的值相同,但是每次調(diào)用函數(shù)返回的函數(shù)是不一樣的
還需要注意的是,如果只是把返回的函數(shù)賦值給變量,那么這個(gè)函數(shù)是不會(huì)執(zhí)行的,直到調(diào)用函數(shù)才會(huì)執(zhí)行:
# -*- coding: utf-8 -*- def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count() print(f1()) print(f2()) print(f3()) #輸出: 9 9 9 可以發(fā)現(xiàn),連續(xù)把count函數(shù)賦值了三次變量后,引用變量時(shí),返回的值全部都是9,這是因?yàn)榉祷睾瘮?shù) f( ) 中調(diào)用了局部變量i,而i是for循環(huán)引用的函數(shù),在賦值 count( ) 函數(shù)到變量時(shí),因?yàn)椴]有直接調(diào)用函數(shù),所以內(nèi)部函數(shù) f( ) 其實(shí)是沒有執(zhí)行的,只是進(jìn)行了循環(huán),而賦值三次后,變量i已經(jīng)循環(huán)到了3,這時(shí)候調(diào)用了函數(shù),內(nèi)部函數(shù) f( ) 在這個(gè)時(shí)候執(zhí)行了,所以三次的結(jié)果都是9
注意:在使用閉包特性時(shí)要記住,返回函數(shù)(內(nèi)部函數(shù))不要引用任何循環(huán)變量或后續(xù)會(huì)發(fā)送變化的變量,如果一定要使用循環(huán)變量怎么辦,可以再創(chuàng)建一個(gè)函數(shù)例如:
# -*- coding: utf-8 -*- def count(): def f(j): def g(): return j * j return g fs = [] for i in range(1,4): fs.append(f(i)) return fs f1,f2,f3 = count() print(f1()) print(f2()) print(f3()) #輸出: 1 4 9 這樣寫,在函數(shù)中就調(diào)用了函數(shù)
2、nonlocal
使用閉包,即內(nèi)部函數(shù)調(diào)用了外部函數(shù)的局部變量,如果只是讀取
外層函數(shù)變量的值,可以看到返回的閉包函數(shù)調(diào)用一切正常:
# -*- coding: utf-8 -*- def inc(): x = 0 def fn(): # 可以看到這里只是讀取了x的值: return x + 1 return fn f = inc() print(f()) print(f()) #輸出 1 1
但是如果要在內(nèi)部函數(shù)去修改
外部函數(shù)變量的值時(shí),會(huì)發(fā)生報(bào)錯(cuò)
# -*- coding: utf-8 -*- def inc(): x = 0 def fn(): # 這里在內(nèi)部函數(shù)修改了外部函數(shù)變量的值 return x = x + 1 return fn f = inc() print(f()) print(f()) #輸出,這里直接就報(bào)錯(cuò)了 File "c:\Users\RZY\Desktop\work\py\test.py", line 5 return x = x + 1 ^ SyntaxError: invalid syntax
上面的原因是因?yàn)?code>x作為局部變量是沒有初始化的,所以直接修改x
變量是不行的,但是可以使用nonlocal
聲明把x
變量初始化,從而可以正常調(diào)用函數(shù)
# -*- coding: utf-8 -*- def inc(): x = 0 def fn(): # 先聲明x變量不是fn函數(shù)的局部變量 nonlocal x x = x + 1 return x return fn f = inc() print(f()) print(f()) #輸出 1 2
注意:使用閉包時(shí),對(duì)外層變量賦值前,需要先使用nonlocal聲明該變量不是當(dāng)前函數(shù)的局部變量,從而時(shí)函數(shù)正常調(diào)用
引用一個(gè)示例:
- 利用閉包返回一個(gè)計(jì)數(shù)器函數(shù),每次調(diào)用它返回遞增整數(shù) # -*- coding: utf-8 -*- def createCounter(): x = 0 def counter(): nonlocal x x = x + 1 return x return counter # 測(cè)試: counterA = createCounter() print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5 counterB = createCounter() if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]: print('測(cè)試通過!') else: print('測(cè)試失敗!') #輸出 1 2 3 4 5 測(cè)試通過! #解析 其實(shí)和上面類似,利用nonlocal聲明之后可以使內(nèi)部函數(shù)修改外部函數(shù)的變量,然后返回一個(gè)函數(shù),從而實(shí)現(xiàn)每次調(diào)用遞增
二、匿名函數(shù)——lambda
- 有些時(shí)候在傳入函數(shù)時(shí),并不需要顯式的定義函數(shù),直接傳入匿名函數(shù)更方便
- 而python中,對(duì)匿名函數(shù)提供了支持,以
map()
為例,在計(jì)算f(x)=x*x
時(shí),除了可以定義一個(gè)函數(shù)f之外,還可以直接傳入匿名函數(shù):
#使用匿名函數(shù): >>> list(map(lambda x:x * x,[1,2,3,4,5,6])) [1, 4, 9, 16, 25, 36] #定義函數(shù): >>> def f(x): ... return x * x ... >>> list(map(f,[1,2,3,4,5,6])) [1, 4, 9, 16, 25, 36] #雖然兩種方法都可以達(dá)到效果,但是可以看出匿名函數(shù)比較簡(jiǎn)潔
從上面的例子可以看出,lambda
關(guān)鍵字就表示匿名函數(shù),而:
前面的x
就表示函數(shù)的參數(shù)匿名函數(shù)有一個(gè)限制,就是只能有一個(gè)表達(dá)式,不需要寫return
返回,返回的值為表達(dá)式的結(jié)果。因?yàn)槟涿瘮?shù)不需要定義函數(shù)名稱,所以也不用擔(dān)心函數(shù)名會(huì)沖突,并且匿名函數(shù)也是一個(gè)函數(shù)對(duì)象,也就是說匿名函數(shù)也可以賦值給一個(gè)變量,通過變量來調(diào)用函數(shù),其實(shí)這個(gè)特性在之前的案例中也使用到了:
>>> f = lambda x : x*x >>> f <function <lambda> at 0x0000020CE841E7A0> >>> f(22) 484 #匿名函數(shù)也可以作為函數(shù)的返回值 >>> def f(x,y): ... return lambda: x * y ... >>> a = f(5,6) >>> a() 30
引用一個(gè)案例
- 利用匿名函數(shù)改造下面代碼,使之更為簡(jiǎn)潔 # -*- coding: utf-8 -*- def is_odd(n): return n % 2 == 1 L = list(filter(is_odd, range(1, 20))) print(L) #輸出: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] - 改造成匿名函數(shù)后: # -*- coding: utf-8 -*- L = list(filter(lambda x:x % 2 ==1, range(1, 20))) print(L) #輸出: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
提示:Python對(duì)匿名函數(shù)的支持有限,只有一些簡(jiǎn)單的情況下可以使用匿名函數(shù)。
到此這篇關(guān)于python返回函數(shù)和匿名函數(shù)的文章就介紹到這了,更多相關(guān)python返回函數(shù)和匿名函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Python進(jìn)行新浪微博的mid和url互相轉(zhuǎn)換實(shí)例(10進(jìn)制和62進(jìn)制互算)
我們?cè)谑褂眯吕宋⒉〢PI時(shí),有時(shí)需要得到一個(gè)微博的url,但是如statuses/public_timeline等接口中取得的微博status的字段中并沒有包含2014-04-04Python中函數(shù)參數(shù)調(diào)用方式分析
這篇文章主要介紹了Python中函數(shù)參數(shù)調(diào)用方式,結(jié)合實(shí)例形式分析了Python函數(shù)參數(shù)定義與使用的四種常見操作方法,需要的朋友可以參考下2018-08-08使用Python實(shí)現(xiàn)獲取文件詳細(xì)信息
Python提供了豐富的內(nèi)置模塊和函數(shù),獲取和操作文件的各種屬性信息,比如大小、修改時(shí)間、權(quán)限以及路徑等,本文將通過詳細(xì)的示例代碼展示如何使用Python中的os和os.path模塊來獲取文件屬性信息,需要的可以參考下2023-12-12Python調(diào)整matplotlib圖片大小的3種方法匯總
我們?cè)谑褂胢atplotlib作圖時(shí),會(huì)遇到圖片不清晰或者圖片大小不是我們想要的,這個(gè)時(shí)候就需要調(diào)整下,這篇文章主要給大家介紹了關(guān)于Python調(diào)整matplotlib圖片大小的3種方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08解決啟動(dòng)django,瀏覽器顯示“服務(wù)器拒絕訪問”的問題
這篇文章主要介紹了解決啟動(dòng)django,瀏覽器顯示“服務(wù)器拒絕訪問”的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-05-05python機(jī)器學(xué)習(xí)之決策樹分類詳解
這篇文章主要介紹了python機(jī)器學(xué)習(xí)之決策樹分類,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12利用python庫(kù)在局域網(wǎng)內(nèi)傳輸文件的方法
今天小編就為大家分享一篇利用python庫(kù)在局域網(wǎng)內(nèi)傳輸文件的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-06-06Python 合并多個(gè)TXT文件并統(tǒng)計(jì)詞頻的實(shí)現(xiàn)
這篇文章主要介紹了Python 合并多個(gè)TXT文件并統(tǒng)計(jì)詞頻的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08