闭包函数是函数嵌套、函数对象、名称空间与做用域结合体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()