python基础语法7 闭包函数与装饰器
闭包函数:
定义:
1 定义再函数内部
2 对外部作用域有引用
闭包函数是 函数嵌套、函数对象、名称空间与作用域 结合体。
创建闭包必须满足以下3点:
- 闭包函数必须有内嵌函数
- 内嵌函数需要引用该嵌套函数上一级变量
- 闭包函数必须返回内嵌函数
# 直接传参 def func(x): print(x) func(1000) # 通过闭包函数传参 def outer(number): # number = 100 # inner就是闭包函数 def inner(): print(number) return inner func = outer(1000) # ---》 inner地址 ---》 func变量名 func() # func ---> inner地址()
闭包函数的应用
# 方式一: 直接传参 def spider_func(url): # 往url地址发送请求,获取响应数据 response = requests.get(url) # 必须接受url # 状态码: 200 if response.status_code == 200: # 获取当前url地址中所有的文本 print(len(response.text)) print(response.text) url = 'https://www.cnblogs.com/xiaoyuanqujing/' spider_func(url) —————————————————————————— # 方式二: 通过闭包函数接受url地址,执行爬取函数 def spider_outer(url): # url = 'https://www.cnblogs.com/xiaoyuanqujing/' def spider_inner(): response = requests.get(url) if response.status_code == 200: print(len(response.text)) return spider_inner # 爬取 小猿取经 spider_blog = spider_outer('https://www.cnblogs.com/xiaoyuanqujing/') spider_blog() # 爬取 京东 spider_baidu = spider_outer('https://www.baidu.com/') spider_baidu()
装饰器:
不修改被装饰对象的源代码
不修改被装饰对象的调用方式
被装饰对象: ---> 需要添加功能 函数
装饰器: ---> 被装饰对象添加的新功能的 函数
time_record ---》 装饰器
需求: 统计下载电影函数的运行时间。
# 下载电影功能(初始代码) def download_movie(): print('开始下载电影...') # 模拟电影下载时间 3秒 time.sleep(3) # 等待3秒 print('电影下载成功...') return 'sean与jason的雨后的小故事.mp4' start_time = time.time() # 获取当前时间戳 download_movie() end_time = time.time() # 获取当前时间戳 print(f'消耗时间: {end_time - start_time}') # 问题: 多个被装饰对象,需要写多次统计时间的代码,导致代码冗余。
# 装饰器: 初级版 def time_record(func): def inner(*args, **kwargs): # 统计开始 start_time = time.time() # 被装饰对象, 问题1: 有返回值, 问题2: 不确定参数的 个数 res = func(*args, **kwargs) # func() ---> download_movie() # 当被统计的函数执行完毕后,获取当前时间 end_time = time.time() # 统计结束,打印统计时间 print(f'消耗时间: {end_time - start_time}') return res return inner download_movie = time_record(download_movie) # inner # name = 'egon' # download_movie(name) download_movie(name='egon') # inner() # inner() ---> download_movie() # download_movie = time_record(download_movie) # inner
# download_movie() # download_movie() ---> download_movie()
# 问题1: 被装饰对象 有返回值 # 下载电影功能 def download_movie(): print('开始下载电影...') # 模拟电影下载时间 3秒 time.sleep(3) # 等待3秒 print('电影下载成功...') return '小泽.mp4' def time_record(func): # func <-- download_movie # 在闭包函数中 def inner(): # 统计开始 start_time = time.time() res = func() # func() ---> download_movie() # 当被统计的函数执行完毕后,获取当前时间 end_time = time.time() # 统计结束,打印统计时间 print(f'消耗时间: {end_time - start_time}') return res return inner download_movie = time_record(download_movie) download_movie()
#问题2: 被装饰对象 有参数 #下载电影功能 import time def download_movie(url): print(f'{url}中的电影开始下载了...') # 模拟电影下载时间 3秒 time.sleep(3) # 等待3秒 print('电影下载成功...') return '小泽.mp4' def time_record(func): # func <-- download_movie # url = 'https://www.baidu.com/' # 在闭包函数中 def inner(url): # 统计开始 start_time = time.time() res = func(url) # func(url) ---> download_movie(url) # 当被统计的函数执行完毕后,获取当前时间 end_time = time.time() # 统计结束,打印统计时间 print(f'消耗时间: {end_time - start_time}') return res return inner download_movie = time_record(download_movie) download_movie(url) --> inner(url) download_movie('https://www.baidu.com')
#问题4: 假如被装饰对象需要接收多个参数 def download_movie(url, url2, url3): print(f'{url}中的电影开始下载了...') # 模拟电影下载时间 3秒 time.sleep(3) # 等待3秒 print('电影下载成功...') return '小泽.mp4' def download_movie(): print('电影开始下载...') # 模拟电影下载时间 3秒 time.sleep(3) # 等待3秒 print('电影下载成功...') return '小泽.mp4'
# 装饰器最终版本 def time_record(func): # func <-- download_movie # 在闭包函数中 def inner(*args, **kwargs): # *args, **kwargs接收所有参数 # 统计开始 start_time = time.time() # 将被装饰对象需要接收的任意参数 原封不动传给func --》 被装饰对象 res = func(*args, **kwargs) # func(url) ---> download_movie(url) # 当被统计的函数执行完毕后,获取当前时间 end_time = time.time() # 统计结束,打印统计时间 print(f'消耗时间: {end_time - start_time}') return res return inner # download_movie = time_record(download_movie) # # download_movie() # download_movie(url) --> inner(url, url2, url3) # download_movie(url1='https://www.baidu.com', url2='url2', url3='url3')
# 装饰器模板:权限设定 # 装饰器最终版本 def wrapper(func): def inner(*args, **kwargs): # 让用户输入内容 username = input('请输入名字') if username == 'tank': # 调用被装饰对象,得到返回值 res = func(*args, **kwargs) return res else: print('用户权限不足!') return inner def func1(): pass func1 = wrapper(func1) func1() # inner()
装饰器的语法糖,是属于装饰器的。
@: 装饰器的语法糖
# 注意: 在使用装饰器语法糖时,装饰器必须定义在被装饰对象之上。
import time # 统计函数执行时间装饰器 def wrapper(func): # 被装饰对象 def inner(*args, **kwargs): # 被装饰对象的参数 # 调用前增加新功能 start_time = time.time() # 调用被装饰对象,并接收返回值 res = func(*args, **kwargs) # 调用后添加新功能 end_time = time.time() print(end_time - start_time) return res return inner # func函数需要执行3秒 # 无参装饰器 # 使用装饰器 @wrapper # wrapper(func) ---> func def func(): time.sleep(3) func()