閉包函數是函數嵌套、函數對象、名稱空間與做用域結合體html
閉包中被內部函數引用的變量,不會由於外部函數結束而被釋放掉,而是一直存在內存中,直到內部函數被調用結束python
# 閉包函數的定義 def func(y): x = 100 def inner(): # 此處的inner 就是閉包函數 print(x) print(y) return inner # 閉包函數的調用 inner = func(1000) inner()
說了這多理論,上代碼。閉包
需求:統計下載媒體文件的時間(簡易版)app
# 需求: 統計下載電影的時間。 # 通常咱們實現這個功能的方式以下 import time def download_movie(): """ 下載電影功能 :return: None """ print("電影下載開始了……") time.sleep(2) # 模擬電影下載2 秒 print("下載完成") start_time = time.time() # 獲取當前時間戳 download_movie() # 下載電影 end_time = time.time() # 獲取當前時間戳 print(f"下載時間:{end_time-start_time}")
以上代碼確實實現了統計下載時間的功能,問題來了,若是我有多個類型文件下載函數,都須要統計時間呢?難道展示咱們的「CV大法」?估計隔天就要被掃地出門了……函數
就沒有什麼辦法能夠解決了嗎?固然有啦!先看代碼url
# 定義一個裝飾器 def time_record(func): """ 新增統計時間功能 :param func: 使用該功能的函數對象 :return: inner 函數對象 """ def inner(): # 統計開始 start_time = time.time() func() # func() ----→ download_movie() or download_music() end_time = time.time() # 統計結束,打印統計時間 print(f"下載時間:{end_time-start_time}") return inner def download_movie(): """ 模擬下載電影 :return: None """ print("電影下載開始了……") time.sleep(2) print("下載完成!") def download_music(): """ 模擬下載音樂 :return: None """ print("歌曲下載開始了……") time.sleep(2) print("下載完成!") # time_record(download_movie) 返回一個inner,將其賦值給download_movie inner = time_record(download_movie) inner() # inner() ----→ download_movie() # time_record(download_music) 返回一個inner,將其賦值給download_music inner1 = time_record2(download_music) inner1()
以上代碼就是對裝飾器的引入,其中 time_record
就是裝飾器,download_movie
和 download_music
就是被裝飾的函數對象。code
經過觀察🕵,以上被裝飾的函數對象是沒有返回值,且沒有參數的。問題來了,假如 download_movie 等被裝飾對象是有返回值和參數,那該咋整?htm
不說廢話🙊,繼續看代碼對象
# 定義一個裝飾器 def time_record(func): """ 新增統計時間功能 :param func: 使用該功能的函數對象 :return: inner 函數對象 """ def inner(*args, **kwargs): # 統計開始 start_time = time.time() # func() ----→ download_movie();定義變量接收download_movie() 的返回值 # 定義可變長位置參數,用於接收任意類型的位置參數,甚至無參均可 # 同理,定義可變長關鍵字參數,用於接收任意類型的關鍵字參數,甚至是無參都行 res = func(*args, **kwargs) end_time = time.time() # 統計結束,打印統計時間 print(f"下載時間:{end_time-start_time}") return res # 將download_movie() 的返回值返回 return inner def download_movie(url): """ 模擬下載電影 :return: None """ print(f"{url}的電影下載開始了……") time.sleep(2) print("下載完成!") return "海賊王.mp4" def download_music(url, name): """ 模擬下載音樂 :return: None """ print(f"{url}中的歌曲下載開始了……") time.sleep(2) print(f"{name}.mp3 下載完成!") download_movie = time_record(download_movie) # 接收download_movie()的返回值 res = download_movie("https://www.cnblogs.com/xiaoyuanqujing/p/11636160.html") print(res) # 打印返回值 download_music = time_record(download_music) download_music("https://music.163.com/", name="煙火裏的塵埃") # 傳入一個位置參數和一個關鍵字參數
細心如你,裝飾器定義好了,可是每次調用都特別麻煩,有沒有一種比較便捷的調用方式呢?blog
噹噹噹當!固然有,那就是裝飾器的語法糖,聽起來就感受使用很是甜,咋用呢?繼續看代碼(因爲以前代碼已經實現了裝飾器,這裏就不重複展現了,只實現語法糖的代碼)
@time_record # 這就是裝飾器time_record 的語法糖 def download_movie(url): """ 模擬下載電影 :return: None """ print(f"{url}的電影下載開始了……") time.sleep(2) print("下載完成!") return "海賊王.mp4" @time_record # 這就是裝飾器time_record 的語法糖 def download_music(url, name): """ 模擬下載音樂 :return: None """ print(f"{url}中的歌曲下載開始了……") time.sleep(2) print(f"{name}.mp3 下載完成!") res = download_movie("https://www.cnblogs.com/xiaoyuanqujing/p/11636160.html") print(res) download_music("https://music.163.com/", name="煙火裏的塵埃") # 傳入一個位置參數和一個關鍵字參數
裝飾器語法糖
@
注意:在使用時,裝飾器必須在被裝飾對象以前定義
裝飾器終極模板
# func 表示須要裝飾的函數對象 def wrapper(func): def inner(*args, **kwargs): # 接收不固定參數 # 調用func 以前增長的新功能 res = func(*args, **kwargs) # 調用被裝飾對象,獲得返回值 # 調用func 以後增長的新功能 return res # 將返回值返回 return inner @wrapper def func1(): pass # 使用裝飾器語法糖後,直接調用函數 func1()