Python 全栈开发:python函数装饰器
装饰器
一、为何要用装饰器
开闭原则(OCP)是面向对象中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。
在python中就是遵循这样的开闭原则
原则: 1.不修改源代码
2.不修改调用方式
目的:在满足1,2原则的基础上,实现功能的扩展
二、什么是装饰器
装饰器是闭包函数的一种应用场景
器指的工具,装饰指的是为被装饰对象添加新功能
完整含义:
装饰器即在不修改被装饰对象源代码与调用方式的前提下,为被装饰器对象添加新功能
装饰器与被装饰的对象均可以是任意可调用的对象
装饰器=》函数
被装饰的对象=》函数
三、装饰器使用
无参装饰器
下面是一个基本的装饰器:计算每个函数执行的时间
import time # 计算每个函数执行的时间 def warpper(func): def inner(): # 函数开始的时间点 start_time = time.time() func() # 被装饰的函数 # 函数结束的时间点 end_time = time.time() # 显示执行时间 total = end_time - start_time # 单位秒 print(total) return inner # 语法糖 @warpper def run(): time.sleep(1) print('刚才在跑步') # 调用函数 run()
对于这个装饰器是怎么来的下面一步步的来讲解
需要实现的功能:计算每个函数执行的时间
原则:不修改源码,不改变调用方式,添加计算时间功能
#1 首先不考虑任何东西,实现计算一个函数执行时间
def run(): # 函数开始的时间点 start_time = time.time() time.sleep(1) print('刚才在跑步') # 函数结束的时间点 end_time = time.time() # 显示执行时间 total = end_time - start_time # 单位秒 print(total) # 调用 run()
功能实现:实现了一个函数的执行时间计算
源代码:被修改
调用方式:没改变
问题:如果有很多地方调用,程序猿就爆炸了。。。。
#2 不让修改源代码,就不改呗
def run(): time.sleep(1) print('刚才在跑步') # 函数开始的时间点 start_time = time.time() # 调用 run() # 函数结束的时间点 end_time = time.time() # 显示执行时间 # 单位秒 print(end_time - start_time)
功能实现:每个函数都要加上这三行代码才能实现功能。。。
源代码:没修改
调用方式:没改变
问题:一个调用改一次,一万个调用,还是炸了。。。
#3 接着改,把函数对象作为参数
def run(): time.sleep(1) print('刚才在跑步') def timer(func): start_time = time.time() func() # 函数结束的时间点 end_time = time.time() # 显示执行时间 total = end_time - end_time # 单位秒 print(end_time - start_time) # 调用 timer(run) # 修改了原函数的调用方式
功能实现:可以实现
源代码:没修改
调用方式:改变
问题:一个调用改一次,一万个调用,还是炸了。。。
#4 只能用闭包函数
# 闭包函数 def wrapper(func): def inner():
start_time = time.time() func() # 原函数
end_time = time.time()
print(end_time - start_time) return inner
run = wrapper(run) #新的run=inner run() #inner()
功能实现:实现
源代码:没修改
调用方式:没改变
问题:run = wrapper(run) 这行代码还是要每个地方就要修改
#5 对于 #4中的问题,python给我们提供了优美的解决方案
@修饰符(语法糖)
修饰符必须出现在函数定义前一行,不允许和函数定义在同一行
一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。
import time # 计算每个函数执行的时间 def warpper(func): def inner(): # 函数开始的时间点 start_time = time.time() func() # 被装饰的函数 # 函数结束的时间点 end_time = time.time() # 显示执行时间 total = end_time - start_time # 单位秒 print(total) return inner # 语法糖 # 装饰器必须在 语法糖前面就要定义好 ,否则找不到装饰器报错 @warpper def run(): time.sleep(1) print('刚才在跑步') # 调用函数 run()
功能实现:实现
源代码:没修改
调用方式:没改变
问题:完美没问题,计算哪个函数的执行时间就给哪个函数加个‘糖’,无需关心哪里调用
无参装饰器升级版(原函数可以任意形参,实现 return )
import time # 计算每个函数执行的时间 def warpper(func): def inner(*args,**kwargs): # 函数开始的时间点 start_time = time.time() res = func(*args,**kwargs) # 原函数有返回值 # 函数结束的时间点 end_time = time.time() # 显示执行时间 total = start_time - end_time # 单位秒 print(total) return res #返回值 return inner # 语法糖 @warpper def run(name): time.sleep(1) print('%s刚才在跑步' % name) return '跑步结束' res = run('fixd') print(res)
无参装饰器模板
#无参装饰器模板 def wrapper(func): def inner(*args,**kwargs): res=func(*args,**kwargs) return res return inner
有参装饰器(带参数的装饰器)
带参数的装饰器:就是给装饰器传参
如果要写一个带参数的装饰器怎么办,那么我们就需要写一个三层的装饰器
import functools def log(k=''): # 这里参数定义的是一个默认参数,如果没有传入参数,默认为空,可以换成其他类型的参数 def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('start') print('{}:{}'.format(k, func.__name__)) # 这里使用了装饰器的参数k r = func(*args, **kwargs) print('end') return r return wrapper return decorator @log() # fun1=log()(fun1) 装饰器没有使用参数 def fun1(a): print(a + 10) fun1(10) # print(fun1.__name__)
# 上面装饰器如果没有@functools.wraps(func)一句的话,这里打印出的函数名为wrapper @log('excute') # fun2=log('excute')(fun2) 装饰器使用给定参数 def fun2(a): print(a + 20) fun2(10)
四、装饰器语法
# 被装饰函数的正上方,单独一行 @deco1 @deco2 @deco3 def foo(): pass foo=deco1(deco2(deco3(foo)))
未完待续