網站首頁 編程語言 正文
此文章繼續上篇高階函數,地址:python函數式編程以及高階函數
一、返回函數
高階函數的特性,除了可以接受函數
作為參數之外,高階函數還可以返回函數
下面來看幾個案例:
1、定義一個求和的函數,可以這樣寫 # -*- 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 但是如果不需要立即求和,而是需要在后面的代碼中再進行計算改怎么辦,當出現這種情況時,就可以不返回求和的結果,而是返回求和的參數,修改后可以這樣寫: # -*- 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 可以看出,當把函數的結果賦值給f時,直接輸出f,返回的是函數,只有在調用 f( ) 時,才會返回結果
看過上面的案例,還可以發現一件事,就是函數內部定義函數是可以直接調用最外層函數的參數的,而在函數內部定義的函數,這種函數又叫內部函數
,最外層的函數叫外部函數
1、閉包
內部函數可以引用外部函數的參數和局部變量
,當外部函數返回內部函數時,相關的參數和變量都保存在返回的內部函數中,這種程序結構又稱為“閉包(Closure)”
上面的內部函數test_sum就引用了局部函數args
需要注意的是每次調用外部函數test_1()時,每次調用都會生成一個新的函數,即便傳入相同的參數:
# -*- 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 可以看到,就算參數相同、返回的值相同,但是每次調用函數返回的函數是不一樣的
還需要注意的是,如果只是把返回的函數賦值給變量,那么這個函數是不會執行的,直到調用函數才會執行:
# -*- 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 可以發現,連續把count函數賦值了三次變量后,引用變量時,返回的值全部都是9,這是因為返回函數 f( ) 中調用了局部變量i,而i是for循環引用的函數,在賦值 count( ) 函數到變量時,因為并沒有直接調用函數,所以內部函數 f( ) 其實是沒有執行的,只是進行了循環,而賦值三次后,變量i已經循環到了3,這時候調用了函數,內部函數 f( ) 在這個時候執行了,所以三次的結果都是9
注意:在使用閉包特性時要記住,返回函數(內部函數)不要引用任何循環變量或后續會發送變化的變量,如果一定要使用循環變量怎么辦,可以再創建一個函數例如:
# -*- 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 這樣寫,在函數中就調用了函數
2、nonlocal
使用閉包,即內部函數調用了外部函數的局部變量,如果只是讀取
外層函數變量的值,可以看到返回的閉包函數調用一切正常:
# -*- coding: utf-8 -*- def inc(): x = 0 def fn(): # 可以看到這里只是讀取了x的值: return x + 1 return fn f = inc() print(f()) print(f()) #輸出 1 1
但是如果要在內部函數去修改
外部函數變量的值時,會發生報錯
# -*- coding: utf-8 -*- def inc(): x = 0 def fn(): # 這里在內部函數修改了外部函數變量的值 return x = x + 1 return fn f = inc() print(f()) print(f()) #輸出,這里直接就報錯了 File "c:\Users\RZY\Desktop\work\py\test.py", line 5 return x = x + 1 ^ SyntaxError: invalid syntax
上面的原因是因為x
作為局部變量是沒有初始化的,所以直接修改x
變量是不行的,但是可以使用nonlocal
聲明把x
變量初始化,從而可以正常調用函數
# -*- coding: utf-8 -*- def inc(): x = 0 def fn(): # 先聲明x變量不是fn函數的局部變量 nonlocal x x = x + 1 return x return fn f = inc() print(f()) print(f()) #輸出 1 2
注意:使用閉包時,對外層變量賦值前,需要先使用nonlocal聲明該變量不是當前函數的局部變量,從而時函數正常調用
引用一個示例:
- 利用閉包返回一個計數器函數,每次調用它返回遞增整數 # -*- coding: utf-8 -*- def createCounter(): x = 0 def counter(): nonlocal x x = x + 1 return x return counter # 測試: 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('測試通過!') else: print('測試失敗!') #輸出 1 2 3 4 5 測試通過! #解析 其實和上面類似,利用nonlocal聲明之后可以使內部函數修改外部函數的變量,然后返回一個函數,從而實現每次調用遞增
二、匿名函數——lambda
- 有些時候在傳入函數時,并不需要顯式的定義函數,直接傳入匿名函數更方便
- 而python中,對匿名函數提供了支持,以
map()
為例,在計算f(x)=x*x
時,除了可以定義一個函數f之外,還可以直接傳入匿名函數:
#使用匿名函數: >>> list(map(lambda x:x * x,[1,2,3,4,5,6])) [1, 4, 9, 16, 25, 36] #定義函數: >>> def f(x): ... return x * x ... >>> list(map(f,[1,2,3,4,5,6])) [1, 4, 9, 16, 25, 36] #雖然兩種方法都可以達到效果,但是可以看出匿名函數比較簡潔
從上面的例子可以看出,lambda
關鍵字就表示匿名函數,而:
前面的x
就表示函數的參數匿名函數有一個限制,就是只能有一個表達式,不需要寫return
返回,返回的值為表達式的結果。因為匿名函數不需要定義函數名稱,所以也不用擔心函數名會沖突,并且匿名函數也是一個函數對象,也就是說匿名函數也可以賦值給一個變量,通過變量來調用函數,其實這個特性在之前的案例中也使用到了:
>>> f = lambda x : x*x >>> f <function <lambda> at 0x0000020CE841E7A0> >>> f(22) 484 #匿名函數也可以作為函數的返回值 >>> def f(x,y): ... return lambda: x * y ... >>> a = f(5,6) >>> a() 30
引用一個案例
- 利用匿名函數改造下面代碼,使之更為簡潔 # -*- 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] - 改造成匿名函數后: # -*- 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對匿名函數的支持有限,只有一些簡單的情況下可以使用匿名函數。
原文鏈接:https://blog.csdn.net/rzy1248873545/article/details/124383732
相關推薦
- 2022-10-27 React事件處理和表單的綁定詳解_React
- 2022-06-28 ASP.NET一次性對GridView批量更新多行數據_實用技巧
- 2022-11-13 yolov5模型配置yaml文件詳細講解_python
- 2022-04-27 Python中的元組(Tuple)操作實例詳解_python
- 2022-06-04 如何通過一篇文章了解Python中的生成器_python
- 2022-09-13 Golang優雅保持main函數不退出的辦法_Golang
- 2022-06-02 C語言基于EasyX庫實現有圖形界面鐘表_C 語言
- 2022-10-03 numpy數組疊加的實現示例_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支