# 装饰器是用来装饰方法的,其作用就是在原函数的基础上,扩展功能。
#之所以要采用装饰器,是因为开放封闭原则,对修改封闭,对扩展开放。也就是说, 新功能的添加不能修改旧代码的执行逻辑和调用方式
import time def func1(n=1): print(f"run start") time.sleep(n) print(f"run end")
#需求:统计该函数执行的时间。我们将通过以下7步实现装饰器实现原理的流程演示
# 修改方式1: 直接在函数调用处,添加扩展代码,这样不会修改源代码,如果有多个地方调用, 会造成重复代码过多,代码冗余。
begin = time.time() func1(2) end = time.time() print(end-begin) begin = time.time() func1(2) end = time.time() print(end-begin) begin = time.time() func1(2) end = time.time() print(end-begin)
# 修改方式2, 在方式1的基础上,将调用的方式封装成方法,减少重复代码
# 带来的有点就是减少了重复代码,但是函数的参数被固化了,如果func1的参数修改了,就会报错。
def wrapper(n): begin = time.time() func1(n) end = time.time() print(end - begin) wrapper(2)
#修改方式3, 在方式2的基础上,利用可变参数对其进行修改
#这种方式,可以完美解决函数入参的修改,但是固化了统计代码执行时间的方法,兵器而只能在func1上使用,如果我们想在func2上也使用该方法的话。那就在加一个wrapper方法。
def wrapper(*args, **kwargs): begin = time.time() func1(*args, **kwargs) end = time.time() print(end - begin) wrapper(2)
#修改方式4,在方式3的基础,再进行封装,使其能够再多个方法上添加该功能
#这里的func1是在wrapper中,属于wrapper方法的局部名称空间,我们要将函数名传入,传入方式有两种,一种是以入参的方式传入,另一种方式就是闭包。
# 可变入参被完整的作为func1的入参传递,无法再将func1作为变量名传入,因此考虑第二种闭包方式传参。为了能够执行,将wrapper进行返回,切记不带括号,带括号就是返回的wrapper的执行结果
def outer(): func_name = func1 def wrapper(*args, **kwargs): begin = time.time() func_name(*args, **kwargs) end = time.time() print(end - begin) return wrapper insper = outer() insper(2) # outer()==>insper insper(2)==> wrapper(2) # 为了可以在多个方法上进行扩展,将func_name的值提取出来作为外部函数的入参 def outer(func_name): def wrapper(*args, **kwargs): begin = time.time() func_name(*args, **kwargs) end = time.time() print(end - begin) return wrapper insper = outer(func1) insper(2) # # outer()==>insper insper(2)==> wrapper(2)
#修改方式5,在方式4的基础,我们已经基本上完成了对函数功能的封装。也可以在多个函数上重复使用该功能。
# 但是现在的调用,不满足我们的开放封闭原则,因为调用方式发生了修改,因此,为了保证调用方式不发生修改,我们再对其进行变形
#outer()的返回值wrapper,就是包装后的func1,表示的是方法的地址,无论命名成什么都可以,为了不修改调用方式,我们将outer()的返回值赋值给func1,就可以保证可最初的调用方式保持一致。
#虽然表面看起来一样,但是其本质已经发生了改变,如果我们将func1被装饰前后的地址打印出来,就会发现,两次的内存地址完全不一样。
def outer(func_name): def wrapper(*args, **kwargs): begin = time.time() func_name(*args, **kwargs) end = time.time() print(end - begin) return wrapper func1 = outer(func1) func1(2)
#修改方式6:修改方式5已经完成了装饰器的基本功能,但是还是有一个缺点,它把函数的返回值给弄丢了,如果func1有返回值的话,按照上边的执行逻辑,是拿不到他的执行结果的。因此还需最后一次变形
# 这就是装饰器最后的完整形态 def outer(func_name): def wrapper(*args, **kwargs): begin = time.time() rlt = func_name(*args, **kwargs) end = time.time() print(end - begin) return rlt return wrapper func1 = outer(func1) func1(2)
#修改方式7 python解释器为了简化上边代码的编写。提供了语法糖的写法,也就是上边的代码的简化写法
def outer(func_name): def wrapper(*args, **kwargs): begin = time.time() rlt = func_name(*args, **kwargs) end = time.time() print(end - begin) return rlt return wrapper @outer def func2(): print("干饭去") time.sleep(1) print("干饭完成") func2()
标签:func1,end,begin,wrapper,outer,time,原理,装饰 From: https://www.cnblogs.com/lhg37927/p/17664722.html