侧边栏壁纸
博主头像
张种恩的技术小栈博主等级

行动起来,活在当下

  • 累计撰写 748 篇文章
  • 累计创建 65 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录

Python基础(15)之装饰器进阶

zze
zze
2019-04-03 / 0 评论 / 0 点赞 / 590 阅读 / 4641 字

不定期更新相关视频,抖音点击左上角加号后扫一扫右方侧边栏二维码关注我~正在更新《Shell其实很简单》系列

获取被包装函数原生属性

例 1:常规函数取函数名

def func():
    print('执行中')
    print(func.__name__)

func()
# result:
#   执行中
#   func

常规函数可以通过函数的 __name__ 属性可拿到当前函数名称。

例 2:被装饰函数取函数名

def wrapper(func):
    def inner():
        print('执行前')
        result = func()
        print('执行后')
        return result
    return inner;
@wrapper
def func():
    print('执行中')
    print(func.__name__)

func()
# result:
#   执行前
#   执行中
#   inner
#   执行后

问题:通过执行结果会发现,结果中想获取的函数名是 func,而实际结果是 inner。原因是 @wrapper 进行包装相当于执行一个操作:func=wrapper(func)=inner

要解决这个问题可以使用 functools 模块:

from functools import wraps

def wrapper(func):
    # <1> 
    @wraps(func)
    def inner():
        print('执行前')
        result = func()
        print('执行后')
        return result

    return inner;

@wrapper
def func():
    print('执行中')
    print(func.__name__)

func()
# result:
#   执行前
#   执行中
#   func
#   执行后

导入 functools 模块后通过 <1> 处操作,会发现执行的函数即使被包装但还是能获取到它本身的属性。

带参数的装饰器

示例

def wrapper(func):
    def inner():
        print('执行前')
        result = func()
        print('执行后')
        return result

    return inner;

@wrapper
def func_1():
    print('执行中')

@wrapper
def func_2():
    print('执行中')

@wrapper
def func_3():
    print('执行中')
...
@wrapper
def func_n():
    print('执行中')

问题:通过上述代码会发现,有很多函数都用了同一个装饰器,如果有一天要取消这些函数上的装饰,就必须对每一个函数进行修改。

解决

定义一个全局变量 flag,并给原本装饰器外部再加一层函数用来接收参数,inner 函数内部通过外部参数 flag 判断被装饰函数的执行与否,修改 flag 即可控制装饰器的执行结果,如下:

from functools import wraps
flag = True

def wrapper_out(flag):
    def wrapper(func):
        @wraps(func)
        def inner():
            if (flag):
                print('{}执行前'.format(func.__name__))
                result = func()
                print('{}执行后'.format(func.__name__))
            else:
                result = func()
            return result

        return inner

    return wrapper

@wrapper_out(flag)
def func_1():
    print('{}执行中'.format(func_1.__name__))

@wrapper_out(flag)
def func_2():
    print('{}执行中'.format(func_2.__name__))

@wrapper_out(flag)
def func_3():
    print('{}执行中'.format(func_3.__name__))

...

@wrapper_out(flag)
def func_n():
    print('{}执行中'.format(func_n.__name__))

func_1()
func_2()
func_3()
func_n()

#result:
    # func_1执行前
    # func_1执行中
    # func_1执行后
    # func_2执行前
    # func_2执行中
    # func_2执行后
    # func_3执行前
    # func_3执行中
    # func_3执行后
    # func_n执行前
    # func_n执行中
    # func_n执行后
from functools import wraps
flag = False

def wrapper_out(flag):
    def wrapper(func):
        @wraps(func)
        def inner():
            if (flag):
                print('{}执行前'.format(func.__name__))
                result = func()
                print('{}执行后'.format(func.__name__))
            else:
                result = func()
            return result

        return inner

    return wrapper

@wrapper_out(flag)
def func_1():
    print('{}执行中'.format(func_1.__name__))

@wrapper_out(flag)
def func_2():
    print('{}执行中'.format(func_2.__name__))

@wrapper_out(flag)
def func_3():
    print('{}执行中'.format(func_3.__name__))

...

@wrapper_out(flag)
def func_n():
    print('{}执行中'.format(func_n.__name__))

func_1()
func_2()
func_3()
func_n()

#result:
    # func_1执行中
    # func_2执行中
    # func_3执行中
    # func_n执行中

多个装饰器装饰同一个函数

代码

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

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

f()
# result:
# wrapper1 ,before func
# wrapper2 ,before func
# in f
# wrapper2 ,after func
# wrapper1 ,after func

图解

image.png

从上图可以看到,从 1-9 步是装饰器的装载过程,10-18 步是执行过程。

结论:多个装饰器装饰同一个函数时,装载顺序是从下到上,但执行顺序却是从上到下,可以理解为创建了一个装饰器栈(先进后出)。

0

评论区