闭包函数与装饰器

 

闭包函数

1、闭包函数必须在函数内部定义
2、闭包函数可以引用外层函数的名字
闭包函数是 函数嵌套、函数对象、名称空间与作用域结合体
# 直接传参
 
def func(x):
print(x)
func(1000)
# 通过闭包函数传参
# 通过闭包函数传参
def outer(number):
# inner就是闭包函数
def inner():
print(number)
 
return inner
 
 
func = outer(1000) # --> inner地址 -->func变量名
func() # func ---> inner地址()

闭与包

基于函数对象的概念,可以将函数返回到任意位置去调用,但作用域的关系是在定义完函数时就已经被确定了的,与函数的调用位置无关
也就是说函数被当做数据处理时,始终以自带的作用域为准,若内嵌函数包含对外部函数作用域(而非全局作用域)中变量的引用,那么该‘内嵌函数’就是闭包函数,简称闭包(closures)

闭包函数的用途

目前为止,我们得到了两种位函数体传值的方式,一种是直接以参数的形式传入,另外一种就是将值包给函数
对比两种方式,方式一在下载同一页面时需要重复传入url,而方式二只需要传一次值,就会得到一个包含指定url的闭包函数,以后调用该闭包函数无需再传url
闭包函数的应用实验
需求:爬取某个网站,打印获取数据的长度
爬虫是获取数据
方式一:直接传参
import requests # 调用requests包
 
 
def spider_func(url):
# 往url地址发送请求,获取响应数据
response = requests.get(url)
# 状态码200
if response.status_code == 200:
print(len(response.text))
# print(response.text)
 
 
spider_func(url)
 
23357
方式二:通过闭包函数接受url地址,执行爬取函数
def spider_outer(url):
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()
 
23357

装饰器

为何要用装饰器?
软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改时封闭的,对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况,对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改
软件包含有的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃,而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器
什么是装饰器?
‘装饰’代指为被装饰对象添加新的功能,‘器’代指器具/工具,装饰器与被装饰的对象均可以是任意调用对象,概括的讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能,装饰器经常用于有切面需求的场景,如:插入日志、性能测试、事物处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用
提示:可调用对象有函数,方法或者类,此外我们单以本章主题函数为例,来介绍函数装饰器,并且被装饰的对象也是函数
总结:
不修改被装饰对象的源代码
不修改被装饰对象的调用方式
被装饰对象:---->需要添加功能,函数
装饰器:----> 被装饰对象添加的新功能 函数

统计电影的下载时间

import time
 
def download_movie():
print('开始下载电影...')
# 模拟电影下载时间 3秒
time.sleep(3) # 等待3秒
print('电影下载成功...')
 
 
start_time = time.time() # 获取当前时间戳
download_movie()
end_time = time.time() # 获取当前时间戳
print(f'消耗时间: {end_time - start_time}')
 
开始下载电影...
电影下载成功...
消耗时间: 3.0001180171966553
装饰器
def download_movie():
print('开始下载电影...')
# 模拟电影下载时间 3秒
time.sleep(3) # 等待3秒
print('电影下载成功...')
 
 
# start_time = time.time() # 获取当前时间戳
# download_movie()
# end_time = time.time() # 获取当前时间戳
# print(f'消耗时间: {end_time - start_time}')
 
 
# 装饰器:
 
def time_record(func):
def inner():
# 统计开始
start_time = time.time()
func() # func() ---> download_movie()
# 当被统计的函数执行完毕后,获取当前时间
end_time = time.time()
# 统计结束,打印统计时间
print(f'消耗时间: {end_time - start_time}')
 
return inner
 
 
inner = time_record(download_movie) # inner
inner() # inner() ---> download_movie()
 
 
开始下载电影...
电影下载成功...
消耗时间: 3.0001163482666016
问题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()
 
 
开始下载电影...
电影下载成功...
消耗时间: 3.000156879425049
问题2 被装饰的对象有参数
def download_movie(url):
print(f'{url}中的电影开始下载了...')
# 模拟电影下载时间 3秒
time.sleep(3) # 等待3秒
print('电影下载成功...')
return '小夏.mp4'
 
 
def time_record(func): # func <-- download_movie
# 在闭包函数中
def inner(url):
# 统计开始
start_time = time.time()
res = func(url) # 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('https://www.baidu.com')
 
 
https://www.baidu.com中的电影开始下载了...
电影下载成功...
消耗时间: 3.000974178314209
问题3:被装饰的对象有多个参数
def download_movie(url):
print(f'{url}中的电影开始下载了...')
# 模拟电影下载时间 3秒
time.sleep(3) # 等待3秒
print('电影下载成功...')
return '小夏.mp4'
 
 
def time_record(func): # func <-- download_movie
# 在闭包函数中
def inner(*args, **kwargs):
# 统计开始
start_time = time.time()
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)
download_movie('https://www.baidu.com')
 
https://www.baidu.com中的电影开始下载了...
电影下载成功...
消耗时间: 3.0000598430633545
装饰器的模板
def wrapper(func):
def inner(*args, **kwargs):
return 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)
 
 
# 不用装饰器
def func2():
time.sleep(3)
 
 
func()
 
 
3.0001182556152344
 

 

posted on 2019-11-12 16:44  Everuse  阅读(133)  评论(0编辑  收藏  举报

导航