Python装饰器

装饰器介绍

啥是装饰器呀?

答:给函数对象增加新功能的一个工具。装饰器的内部函数其实就是一个闭包函数。

装饰器有啥原则呀?

答:利用装饰器增加新功能时,不修改被装饰对象的源代码,不改变被装饰对象的调用方式。

简单装饰器示例

有一个函数:
def func():
    print('我是一个函数')

给这个函数加个功能,计算该函数运行时间。给一个函数加功能,不改变函数源代码,不改变函数调用方式,使用装饰器!
import time
def count_time(func):
    def wrapper():
        start = time.time()
        time.sleep(2)
        func()
        print('函数执行消耗的时间为:%s' % (time.time()-start))
    return wrapper

func = count_time(func)   #本质上是返回wrapper的函数。

func()   #本质上调用的是 wrapper(), wrapper里面的func()才能调用的原func函数。

装饰器语法糖:为了直观,python 为了省略 func = count_time(func)这一步,提出了语法糖的概念。

import time
def count_time(func):
    def wrapper():
        start = time.time()
        time.sleep(2)
        func()
        print('函数执行消耗的时间为:%s' % (time.time()-start))
    return wrapper

@count_time  #重点:这句话等同于: func = count_time(func)
def func():
    print('我是一个函数')

func()   #本质上调用的还是count_time(func)返回的 wrapper(), wrapper里面的func()才能调用的原func函数。

当被装饰器有参数和返回值的时候该怎么办?

这里介绍一下牛逼的装饰器模板,直接用就完事了。推导过程就不写了,累了,等我想起来了再说吧...(ps: 虽然模板是抄的,但程序员嘛,程序员抄东西,能叫抄吗?不能,这叫借鉴。)

def outter(func):
    def wrapper(*args,**kwargs):
        print('执行被装饰函数之前 你可以做的操作')
        res = func(*args,**kwargs)
        print('执行被装饰函数之后 你可以做的操作')
        return res
    return inner

@outter  # 恒等于 func = outter(func) 返回wrapper这个闭包函数。
def func(a, b, c, d, e, f)
    print(a, b, c, d, e, f)
    return '我是一个帅比'
    
#这里调用的时候
func(1,2,3,4,5,6)  #就相当于 调用wrapper(1, 2, 3, 4, 5, 6)  

多层装饰器

装饰器在装饰的时候 顺序从下往上。如👇所示:装饰的时候先调用 outtter3(), 再outtter2(), 在outtter3()
装饰器在执行的时候 顺序从上往下。

那么,装饰器什么时候执行呢?就是被装饰函数被调用的时候。如下,调用index,index(), 然后执行多层装饰器时,会先调用最外面的,也就是 outter1(), outter2(), 最后outter3()

def outter1(func1):
    print('加载了outter1')
    def wrapper1(*args,**kwargs):
        print('执行了wrapper1')
        res1=func1(*args,**kwargs)
        return res1
    return wrapper1
def outter2(func2):
    print('加载了outter2')
    def wrapper2(*args,**kwargs):
        print('执行了wrapper2')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2
def outter3(func3):
    print('加载了outter3')
    def wrapper3(*args,**kwargs):
        print('执行了wrapper3')
        res3=func3(*args,**kwargs)
        return res3
    return wrapper3

@outter1
# index = outter1(wapper2) @outter2 # wrapper2 = outter2(wrapper3) @outter3 # wrapper3 = outter3(最原始的index函数内存地址) def index(): print('from index') index() >>>加载了outter3 >>>加载了outter2 >>>加载了outter1 >>>执行了wrapper1 >>>执行了wrapper2 >>>执行了wrapper3 >>>from index

再来一个例子:

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()

>>>wrapper2 ,before func
>>>wrapper1 ,before func
>>>in f
>>>wrapper1 ,after func
>>>wrapper2 ,after func

#注意,这个例子的wrapper,只有被装饰器执行调用的时候才被调用。

 装饰器传参

当想给装饰器传个参数的时候,我们知道,传参数的方式有两种:1、普通函数传参。2、闭包传参。

如果我们给装饰器后面写个括号,传参数,装饰器不就被调用了,况且装饰器里面的参数只能有一个,那就是被装饰的那个函数。

所以这时候,我们选择闭包传参。怎么闭包传参呢?

在装饰器外面再套一层函数不就OK了。如下所示:

def outer_two(a, b, c):
    def outer(func):
        def wrapper(*args, **kwargs):
            
            if a == 1:
                res = func(*args, **kwargs)
                return res
            else:
                print('如果a不等于1执行的操作')
        
        return wrapper

    return outer
        
        
@outer_two(1, 2, 3)   #这个就等于 outer, 因为outer_tow调用时的返回值时outer
def func():
    print('被装饰器')    

 装饰器补充点

由来:使用装饰器有一个小毛病,就是想要查看被装饰器的注释或者名字时,比如使用如下方法时:

print(help(index)) # 查看函数的注释
print(index.__name__) # 查看函数名字符串形式

结果看到的注释不是被装饰器的注释,而是装饰器里面的内部闭包函数的注释。

查看到的函数名字也是装饰器内部函数的名字,这样我就不爽了,我调用函数的时候,想看函数的名字和注释,看到的却不是对的。

基于以上情况,Python提供了解决方案,叫做装饰器修复技术。

from functools import wraps
def outter(func):
    @wraps(func)  # 装饰器修复技术
    def inner(*args,**kwargs):
        """
        我是inner函数
        :param args:
        :param kwargs:
        :return:
        """
        print('执行被装饰函数之前 你可以执行的操作')
        res = func(*args,**kwargs)
        print('执行被装饰函数之后 你可以执行的操作')
        return res
    return inner

@outter  # index = outter(最原始的index内存地址)
def index():
    """
    这是index函数
    :return:
    """
    pass

#注意wraps使用的位置!

这时候:

用户查看被装饰函数的函数名的时候查看到的就是被装饰函数本身
用户查看被装饰函数的注释的时候查看到的就是被装饰函数的注释

 

posted @ 2019-07-12 00:40  KbMan  阅读(238)  评论(0编辑  收藏  举报