python閉包和裝飾器

DAY 9. 閉包和裝飾器

9.1 閉包

閉包就是內部函數對外部函數作用域內變量的引用

可以看出

  • 閉包是針對函數的,還有兩個函數,內部函數和外部函數
  • 閉包是為了讓內部函數引用外部函數作用域內的變量的

我們先寫兩個函數

def fun1():print("我是fun1")def fun2():print("我是fun2")

這樣fun2就作為fun1的內部函數,此時在函數外部是無法調用Fun2的,因為

  1. fun2實際上相當于fun1的一個屬性(方法),作用域是fun1的塊作用域,全局作用域中無法找到,
  2. 函數內屬性的生命周期是在函數運行期間,在fun1中只是定義了fun2,并沒有調用它

為了讓fun2跳出fun1的生命周期,我們需要返回fun2,這樣在外部獲取到的fun1的返回值就是fun2,這樣調用fun1的返回值就是調用了fun2,如:

def fun1():print("我是fun1")def fun2():print("我是fun2")return fun2var = fun1()
var()
# 我是fun1
# 我是fun2

當然,這還不是一個閉包,閉包是引用了自由變量的函數,所謂自由變量可以理解為局部變量,如果fun2調用了fun1中的變量,那么fun2就是一個閉包了。如

def fun1(var1):def fun2():print(f"var1 = {var1}")return fun2var = fun1(1)
var()  # var1 = 1

閉包的作用

閉包私有化了變量,實現了數據的封裝,類似于面向對象

def fun1(obj):def fun2():obj[0] += 1print(obj)return fun2if __name__ == '__main__':mylist = [i for i in range(5)]var = fun1(mylist)var()var()var()# [1, 1, 2, 3, 4]# [2, 1, 2, 3, 4]# [3, 1, 2, 3, 4]

9.2 裝飾器

閉包在python中有一個重要的用途就是裝飾器,裝飾器接受被裝飾的函數作為參數并執行一次調用,裝飾器的本質還是一個閉包

def func1(func):def func2():print("func2")return func()return func2@func1
def Demo():print("Demo")if __name__ == '__main__':Demo()# func2# Demo
  • 首先,@func1是一顆語法糖,等價于func1(Demo)()
  • 外部函數必須能接收一個參數,也只能接受一個參數,如果有多個參數,必須再套一個函數,因為在使用@語法糖時,會自動把被修飾函數作為參數傳遞給裝飾器
  • 內部函數必須返回被裝飾函數的調用

運行流程:

  1. 把被修飾函數作為參數傳遞給裝飾器,這時函數返回的是閉包函數func2
  2. 隱式地調用func2,相當于func2(),執行函數體,輸出func2,這時函數返回值是func(),返回的直接是被修飾函數的調用,相當于直接執行被修飾函數,輸出Demo

相當于:

def func1(func):def func2():print("func2")return func()return func2# @func1
def Demo():print("Demo")if __name__ == '__main__':# s = Demo()# 先把被修飾函數作為參數傳遞給修飾器,這里的s就是func2s = func1(Demo)# 調用閉包函數s()print(s)# func2# Demo# <function func1.<locals>.func2 at 0x00000117F163AD90>

9.2.1 裝飾器帶參數

def func1(num):def func2(func):def func3():if num >10:print("大于10")else:print("小于10")return func()return func3return func2@func1(num=12)
def Demo():print("Demo")if __name__ == '__main__':Demo()b

執行流程

  1. 將裝飾器的參數傳遞給第一層函數,并返回第二層函數func2
  2. 將被修飾函數作為參數傳遞給第二層函數func2,隱式調用func2,返回閉包函數
  3. 執行閉包函數,并返回被修飾函數的調用(執行被修飾函數)

9.2.2 被修飾函數帶參數

如果被修飾函數帶有參數,需要把參數傳遞給內層閉包函數,返回被修飾函數的調用時記得加參數

def func1(func):def func2(arg):arg += 1# 記得加參數return func(arg)return func2@func1
def Demo(arg):print(arg)if __name__ == '__main__':Demo(11)  # 12

9.2.3 例

  1. 求斐波那契數列任意一項的值
import timedef code_time(func):'''修飾器,用來打印函數運行時間:param func: 被修飾函數:return: func'''start_time = time.time()def closer(*args,**kwargs):result = func(*args,**kwargs)codeTime = time.time() - start_timeprint(f"This code runs at:{codeTime}")return resultreturn closerdef _Fibonacci(n):if n <= 1:return 1else:return _Fibonacci(n-1) + _Fibonacci(n-2)@code_time
def Fibonacci(n):return _Fibonacci(n)if __name__ == '__main__':var = Fibonacci(40)print(var)# This code runs at:61.738335609436035# 165580141

發現代碼效率非常低,輸出第四十個值需要一分多鐘,這是應為每計算一個值,需要計算前兩個值,這里有很多重復的,如

            10||-----------------|9                 8
|--------|       |--------|
8        7       7        67,8被重復計算多次

所以需要把已經計算過的儲存起來,計算之前先判斷有沒有計算過,沒計算過再計算,修改程序為:

import timedef code_time(func):'''修飾器,用來打印函數運行時間:param func::return:'''start_time = time.time()def closer(*args,**kwargs):result = func(*args,**kwargs)codeTime = time.time() - start_timeprint(f"This code runs at:{codeTime}")return resultreturn closer
resultList = {0:1,1:1}
def _Fibonacci(n):if n <= 1:return 1else:if n-1 in resultList:a = resultList[n-1]else:a = _Fibonacci(n-1)resultList[n-1] = aif n-2 in resultList:b = resultList[n-2]else:b = _Fibonacci(n-2)resultList[n-2] = breturn a + b@code_time
def Fibonacci(n):return _Fibonacci(n)if __name__ == '__main__':var = Fibonacci(40)print(var)# This code runs at:0.0# 165580141

速度快了很多,但重復的代碼是不能忍受的,使用修飾器重新一下:

import timedef code_time(func):start_time = time.time()def closer(*args, **kwargs):result = func(*args, **kwargs)codeTime = time.time() - start_timeprint(f"This code runs at:{codeTime}")return resultreturn closerdef modify(func):catch = {0: 1, 1: 1}def closer(*args):if args not in catch:catch[args] = func(*args)return catch[args]return closer@modify
def _Fibonacci(n):if n <= 1:return 1else:return _Fibonacci(n - 1) + _Fibonacci(n - 2)@code_time
def Fibonacci(n):return _Fibonacci(n)if __name__ == '__main__':var = Fibonacci(40)print(var)

有20節樓梯,一次可以走1,2,3,4級,總共有多少種走法

from my_python_package import code_timedef Modify(c = None):if c == None:c = {}def modify(func):catch = cdef closer(*args):if args[0] not in catch:catch[args[0]] = func(*args)return catch[args[0]]return closerreturn modify@Modify()
def _Stairs(num, steps):count = 0if num == 0:count = 1elif num > 0:for step in steps:count += _Stairs(num-step,steps)return count@code_time
def Stairs(num,steps):count = _Stairs(num,steps)return countif __name__ == '__main__':num = 20steps = [step for step in range(1,5)]count = Stairs(num, steps)print(count)# Stairs runs at: 0.0 s# 283953

9.3 總結

  • 閉包:內部函數調用了外部函數作用域內的變量

    • 針對函數
    • 要有自由變量(私有變量)
    • 要點:內部函數要跳出外部函數的生命周期,需要外部函數把他return出來
  • 裝飾器:

    • 基礎:閉包
    • 作用:不修改原來代碼的基礎上拓展原函數功能
    • 用處:修改API功能,AOP編程
    • 要點:@語法糖,函數執行順序
  • 參考鏈接

Python高級編程技巧(進階)(已完結)

Python的閉包與裝飾器

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457585.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457585.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457585.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

學歷是銅牌,能力是銀牌,人脈是金牌,思維是王牌

有人工作&#xff0c;有人上學&#xff0c;大家千萬不要錯過這篇文章&#xff0c;能看到這篇文章也是一種幸運&#xff0c;真的受益匪淺&#xff0c;對我有很大啟迪&#xff0c;這篇文章將會改變你我的一生&#xff0c;真的太好了&#xff0c;希望與有緣人分享&#xff0c;也希…

石頭剪刀布python編程_《python核心編程第二版》練習題——游戲:石頭剪刀布

習題里比較有意思的一個題目&#xff0c;實現石頭剪刀布這個游戲&#xff0c;起初設計的時候走彎路了(主要時被習題里那個“盡量少用if判斷”給整暈了)&#xff0c;想的太復雜&#xff0c;后來發現其實非常簡單&#xff0c;完全可以不寫if語句。還是枚舉法&#xff1a;#! /usr/…

SpringMvc面試題

f-sm-1. 講下SpringMvc和Struts1,Struts2的比較的優勢 性能上Struts1>SpringMvc>Struts2 開發速度上SpringMvc和Struts2差不多,比Struts1要高f-sm-2. 講下SpringMvc的核心入口類是什么,Struts1,Struts2的分別是什么 SpringMvc的是DispatchServlet,Struts1的是ActionServl…

python 鴨子類型

DAY 10. 鴨子類型 這個概念來源于美國印第安納州的詩人詹姆斯惠特科姆萊利&#xff08;James Whitcomb Riley,1849-1916&#xff09;的詩句&#xff1a;”When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.”…

thinkphp一句話疑難解決筆記

URL_PATHINFO_DEPR, depr表示 網頁路徑"分隔符",用"-", 有利于seo,注意是從 sername/index.php(開始的)/home-user-login-var-value開始的,pathinfo也支持普通的參數傳值(僅僅支持參數...). 在thinkphp中,有兩個地方使用depr,另一個就是tpl的文件目錄組織分…

python選取特定行_pandas.DataFrame選取/排除特定行的方法

pandas.DataFrame選取特定行使用Python進行數據分析時&#xff0c;經常要使用到的一個數據結構就是pandas的DataFrame&#xff0c;如果我們想要像Excel的篩選那樣&#xff0c;只要其中的一行或某幾行&#xff0c;可以使用isin()方法&#xff0c;將需要的行的值以列表方式傳入&a…

學校選址_洛谷U3451_帶權中位數

題目描述 在一條大路一旁有許多棟樓&#xff0c;每棟樓里有許多小學生&#xff08;哈哈哈一波小學生來襲&#xff01;&#xff09;。但是這條路上沒有小學&#xff01;&#xff01;&#xff01;&#xff01;所以唯恐世界不亂的牛A打算在路上&#xff08;汽車什么的都不敢來這個…

python 重載的實現(single-dispatch generic function)

DAY 11. python 重載 函數重載是指允許定義參數數量或類型不同的同名函數&#xff0c;程序在運行時會根據所傳遞的參數類型選擇應該調用的函數 &#xff0c;但在默認情況下&#xff0c;python是不支持函數重載的&#xff0c;定義同名函數會發生覆蓋 def foo(a:int):print(fin…

SQL中的多表查詢,以及JOIN的順序重要么?

說法是&#xff0c;一般來說&#xff0c;JOIN的順序不重要&#xff0c;除非你要自己定制driving table。 示例&#xff1a; SELECT a.account_id, c.fed_id, e.fname, e.lname-> FROM account AS a INNER JOIN customer AS c-> ON a.cust_id c.cust_id-> INNER JOIN …

python可變對象 不可變對象_【Python】可變對象和不可變對象

在 Python 中一切都可以看作為對象。每個對象都有各自的 id, type 和 value。id: 當一個對象被創建后&#xff0c;它的 id 就不會在改變&#xff0c;這里的 id 其實就是對象在內存中的地址&#xff0c;可以使用 id() 去查看對象在內存中地址。type: 和 id 一樣當對象唄創建之后…

MySQL 調優基礎(三) Linux文件系統

Linux的文件系統有點像MySQL的存儲引擎&#xff0c;它支持各種各樣的文件系統。它最上層是通過 virtual files system虛擬文件系統作為一個抽象接口層來對外提供調用的。然后下層的各種文件系統實現這些調用接口就行了。 1. Linux 中的 日志文件系統和非日志文件系統 文件內容的…

python 經典類和新式類

DAY 12. python新式類和舊式類 繼承自object基類的類叫做新式類&#xff0c;否則叫做舊式類&#xff0c;python3中的類默認是新式類&#xff0c;之前版本默認是舊式類 rootkail:~# python python 2.7.15 (default,Jul 28 2018,11:29:29) [GCC 8.1.0] on linux2 Type "he…

Why does pthread_cond_signal not work?【轉】

轉自&#xff1a;http://stackoverflow.com/questions/16819169/why-does-pthread-cond-signal-not-work# 0 down vote favorite I am currently learing all around POSIX threads (pthread). I now have created a simple program which increased a shared value by 7 until…

Android開發技術周報 Issue#72

新聞 Android N 最初預覽版&#xff1a;開發者 API 和工具教程 Gradle依賴的統一管理 理解Java垃圾回收機制 淺談 Android 編程思想和架構 由Android 65K方法數限制引發的思考 Android音頻開發&#xff08;1&#xff09;&#xff1a;基礎知識 Android音頻開發&#xff08;…

python 單例模式的四種實現方法

DAY 13. 單例設計 13.1 什么是單例設計 一個類每次實例化返回的都是同一個對象&#xff0c;這種設計模式叫做單例設計&#xff0c;這個類叫做單例類 13.2 實現單例設計的方法 13.2.1 重寫__new__() class Foo:def __new__(cls,*args, **kwargs):# 如果是第一次實例化&…

Redis3.2.5部署(單節點)

1.安裝jdk1.8 [rootsht-logstash-01 ~]# cd /usr/java/ [rootsht-logstash-01 java]# wget --no-check-certificate --no-cookies --header "Cookie: oraclelicenseaccept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u111-b14/jdk-8u111…

字節跳動 設計模式 pdf_憑這份pdf我拿下了美團、字節跳動、阿里、小米等大廠的offer...

關于程序員&#xff0c;除了做項目來提高自身的技術之外&#xff0c;還有一種提升自己的專業技能就是&#xff1a;多&#xff01;看&#xff01;書&#xff01;小編整理出一篇Java進階架構師之路的核心知識&#xff0c;同時也是面試時面試官必問的知識點&#xff0c;篇章也是包…

B. One Bomb (#363 Div.2)

B. One Bombtime limit per test1 secondmemory limit per test256 megabytesinputstandard inputoutputstandard outputYou are given a description of a depot. It is a rectangular checkered field of n??m size. Each cell in a field can be empty (".") or…

力扣交替打印FooBar

這道題要注意的是兩個線程喚醒和等待的順序&#xff0c;應為第一個線程會比第二個線程更早結束&#xff0c;所以如果第一個線程已經結束&#xff0c;而第二個線程還在等待被喚醒&#xff0c;那第二個線程會一直等待下去&#xff0c;因此第一個線程要先等待后喚醒&#xff0c;這…

項目開發容易出錯情況統計

2016年11月17日 11:30:45 星期四 1.適配&#xff1a; a) APP彈窗大屏幕適配&#xff08;例如&#xff0c; 是否居中&#xff09; 2.按鈕狀態&#xff1a; a) 按鈕點擊后沒有disable 如果新頁面加載卡頓導致用戶多次點擊&#xff0c;生成多次請求 b) 按鈕disable后什么時候enabl…