装饰器 原理

# 装饰器是用来装饰方法的,其作用就是在原函数的基础上,扩展功能。
#之所以要采用装饰器,是因为开放封闭原则,对修改封闭,对扩展开放。也就是说, 新功能的添加不能修改旧代码的执行逻辑和调用方式

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()

 

posted @ 2023-08-29 14:39  往昔遗忘  阅读(20)  评论(0编辑  收藏  举报