python全栈-Day 12

一、完美的装饰器(纠正装饰器的函数指向)

1、装饰器的前后执行的解释

def wrapper(f):
    def inner(*args,**kwargs):  #定义函数的时候,属于接收参数,此时  *args,**kwargs聚合为元组,字典
        print('装饰在函数前的')
        print(args)  #args参数本身,依旧是 元组
        print(*args)
        ret = f(*args,**kwargs)  #调用函数的时候,属于使用参数,此时  *args,**kwargs拆分为元组的各元素,字典的各key-value
        print('装饰在函数后的')
        return ret
    return inner

@wrapper   #fun = wrapper(fun)    #装饰器只是一个传话的媒介
def fun(a,b):
    print('fun开始了')
    print('a',a)
    print('b',b)
    return 'fun返回值'

re = fun('wangjing',1234)
print(re)

2、此处添加2个知识点

def wahaha():
    '''
    一个打印娃哈哈的函数
    :return:
    '''
    print('娃哈哈')

print(wahaha.__name__)   #函数的名称的字符串格式
print(wahaha.__doc__)   #函数的注释
import os
print(os.path.getsize('web_cache') )  #获取文件大小

3、查看基本的装饰器对 原函数产生的变化

注意结果中的 fun.__name__ 的值已经变成了inner!!!!也就是说虽然添加了装饰器wrapper的功能,但是fun函数已经不存在,而是变成了inner

def wrapper(f):
    def inner(*args,**kwargs):  #定义函数的时候,属于接收参数,此时  *args,**kwargs聚合为元组,字典
        '''
        这是inner自己的注释
        :param args:
        :param kwargs:
        :return:
        '''
        print('装饰在函数前的')
        print(args)  #args参数本身,依旧是 元组
        print(*args)
        ret = f(*args,**kwargs)  #调用函数的时候,属于使用参数,此时  *args,**kwargs拆分为元组的各元素,字典的各key-value
        print('装饰在函数后的')
        return ret
    return inner

@wrapper   #fun = wrapper(fun)    #装饰器只是一个传话的媒介
def fun(a,b):
    '''
    这是fun原来的的注释
    :param a:
    :param b:
    :return:
    '''
    print('fun开始了')
    print('a',a)
    print('b',b)
    return 'fun返回值'

re = fun('wangjing',1234)
print(re)
print(fun.__name__)    #实际上fun的函数名已经变成了inner,因为在全局名字空间中没有fun的函数名,fun作为wrapper的参数存储

 4、添加一个第三方的装饰器,使fun函数完全不变化,依旧是他本身。即在inner函数上添加 一个第三方的带参数的装饰器

from functools import wraps
def wrapper(f):
    @wraps(f)    #是一个第三方的装饰器,作用是 完全不改变fun的本质,但是依旧可以实现装饰器wrapper的功能
    def inner(*args,**kwargs):  #定义函数的时候,属于接收参数,此时  *args,**kwargs聚合为元组,字典
        '''
        这是inner自己的注释
        :param args:
        :param kwargs:
        :return:
        '''
        print('装饰在函数前的')
        print(args)  #args参数本身,依旧是 元组
        print(*args)
        ret = f(*args,**kwargs)  #调用函数的时候,属于使用参数,此时  *args,**kwargs拆分为元组的各元素,字典的各key-value
        print('装饰在函数后的')
        return ret
    return inner

@wrapper   #fun = wrapper(fun)    #装饰器只是一个传话的媒介
def fun(a,b):
    '''
    这是fun原来的的注释
    :param a:
    :param b:
    :return:
    '''
    print('fun开始了')
    print('a',a)
    print('b',b)
    return 'fun返回值'

re = fun('wangjing',1234)
print(re)
print(fun.__name__)    #此时fun的函数名依旧是本身的fun

二、带参数的装饰器

给个场景:给函数计算执行时间,之后可能又把这些装饰作用在全部函数中删除

1、先写一个函数:不包含集体删除的功能,写一个装饰器可计算函数执行时间

import time
def timer(func):
    def inner(*args,**keargs):
        start = time.time()
        ret = func(*args,**keargs)
        end = time.time()
        print(end - start)
        return ret
    return inner

@timer
def wahaha():
    time.sleep(0.1)
    print('wahaha')

@timer
def shuangww():
    time.sleep(0.2)
    print('shuangww')

wahaha()
shuangww()

 2、再写一个函数:包含集体删除的功能,写一个装饰器可计算函数执行时间,并且可以通过一个布尔变量的值 控制是否进行删除的功能

import time
flag = False
def timer_out(flag):
    def timer(func):
        def inner(*args,**keargs):
            if flag:
                start = time.time()
                ret = func(*args,**keargs)
                end = time.time()
                print(end - start)
                return ret
            else:
                ret = func(*args,**keargs)
                return ret
        return inner
    return timer

@timer_out(flag)  #装饰器的 函数名=括号,则代表函数执行,先执行函数(timer = timer_out(flag))再进行装饰器操作(实际上等于 @timer,即wahaha = timer(wahaha))
def wahaha():
    time.sleep(0.1)
    print('wahaha')

@timer_out(flag)
def shuangww():
    time.sleep(0.2)
    print('shuangww')

wahaha()
shuangww()

 3、最后凑数总结一下逻辑走向

在1中的装饰器的外层再加一个函数,该函数有一个 参数flag,返回值为 timer函数~~

装饰器的装饰方式改为:@timer_out(flag)

装饰器的 函数名=括号,则代表函数执行,先执行函数(timer = timer_out(flag))再进行装饰器操作(实际上等于 @timer,即wahaha = timer(wahaha))

三、多个装饰器装饰一个函数

def wrapper1(func):  #func-->innner2
    def inner1():
        print('inner1:before func')
        func()   #f
        print('inner1:after func')
    return inner1

def wrapper2(func):  #func-->f
    def inner2():
        print('inner2:before func')
        func()
        print('inner2:after func')
    return inner2

@wrapper1
@wrapper2
def f():
    print('f被执行了')

f()

 最后凑一下逻辑走向:

1、把wrapper1放到内存里

2、把wrapper2放到内存里

3、处理装饰器,按照从上到下的执行原则,走到@wrapper1,但是装饰器必须得找到距离最近的函数才能真正发生作用,因此走到了@wrapper2,即 

f = wrapper2(f)== inner2

此时wrapper1的形参func指向了f

4、之后由于最近的函数已经找到了,所以再执行@wrapper1,此时f已经指向inner2了,此时所以

#f = wrapper1(f)-->wrapper1(inner2)== inner1

此时wrapper2的形参func指向了inner2 

5、此时走到了f(),即调用f函数,此时f函数已经指向了inner1,最终的执行是 inner1,注意执行的inner1中的func函数指的是 inner2,所以最终执行结果是:

inner1:before func
inner2:before func
f被执行了
inner2:after func
inner1:after func

注意:装饰器的部分就只是各自指来指去,只有到f()才有真实的执行,才有了打印结果,也就是说最终只执行了一个部分:inner1,其中inner1使用了一个 上层局部变量:func,对应func指的是 inner2。三层的装饰器就类比即可,所以只记现象就可以了

使用场景:比如想要写两个装饰器,1记录用户登录情况,2计算登录函数执行时间,这个时候2一定作为前一个装饰器

再以上基础上把装饰器的返回值加上,结果返回值用的是 f的哟“hahaha”

def wrapper1(func):  #func-->innner2
    def inner1():
        print('inner1:before func')
        ret = func()   #f
        print('inner1:after func')
        return ret
    return inner1

def wrapper2(func):  #func-->f
    def inner2():
        print('inner2:before func')
        ret = func()
        print('inner2:after func')
        return ret
    return inner2

@wrapper1
@wrapper2
def f():
    print('f被执行了')
    return 'hahahha'

print(f())
原文地址:https://www.cnblogs.com/txbbkk/p/9410941.html