Python装饰器函数进阶用法

python装饰器可以解决了在不修改函数代码的情况下,对函数功能拓展,但是之前的装饰器函数也存在一定的问题,还有多个装饰器同时装饰一个函数的情况。

python

0x00 返回原函数名称

之前的函数通过装饰器,可以实现了在使用原函数的func()的时候,通过@wrapper语法糖后,实现了功能的扩展,但是在调用一些类似__name__、__doc__函数的时候,返回的信息是inner()函数的信息。不过可以通过functools模块的wraps来实现完美的对函数进行装饰。

import time
from functools import wraps

# 新增功能
def timer(func):
    @wraps(func)
    def inner(a):
        start_time = time.time()
        ret = func(a)
        end_time = time.time()
        print(end_time - start_time)
        return ret

    return inner


# 已有函数
@timer  # 语法糖
def hw(a):
    time.sleep(5)
    print('Hello World!', a)
    return 'result'


print(hw.__name__)  #hw

这样通过在inner()的上方加上语法糖@wrap(),不过该处语法糖需要加入被装饰的函数名作为参数。这样在调用__name__、__doc__的时候,返回的信息就是原函数的了,而不是inner的。

0x01 带参数的装饰器

由于之前的装饰器函数中的参数为原函数名称,为了加入一个新的变量时候,可以通过闭包的特性来引入一个变量,不过这样装饰器就有3层函数了,当然一般情况下都不会弄那么多层嵌套。

import time
from functools import wraps

flag = True

# 新增功能
def outter(flag):
    def timer(func):
        @wraps(func)
        def inner(a):
            if flag:
                start_time = time.time()
                ret = func(a)
                end_time = time.time()
                print(end_time - start_time)
            else:
                ret =func(a)
            return ret
        return inner
    return timer

# 已有函数
@outter(flag)  # 语法糖
def hw(a):
    # time.sleep(5)
    print('Hello World!', a)
    return 'result'

hw(4)   #flag = True,有新功能

当然一般这种情况基本不会出现,只是为了学习才。。。主要目的是为装饰器引入一个参数。其次在被装饰函数上方的语法糖也应该相应变为@outter(flag),不要忘记在语法糖中加入引入的参数。

0x02 多个装饰器装饰一个函数

对于多个装饰器装饰同一个函数的时候,由于变量与函数内存地址之间关系较为复杂,不过执行顺序较为有规律,可以只记忆执行顺序即可。

from functools import wraps

def add_func1(func):
    @wraps(func)
    def inner():
        print('func1')
        func()
        print('func1')
    return inner

def add_func2(func):
    @wraps(func)
    def inner():
        print('func2')
        func()
        print('func2')
    return inner

@add_func2
@add_func1
def my_func():
    print('This is function')

my_func()

'''
func2
func1
This is function
func1
func2
'''

在内存中的加载顺序为先加载def add_func1(func),之后加载def add_func2(func),之后加载语法糖装饰器,顺序为@add_func1后才@add_func2,这里顺序比较奇怪,语法糖总是先加载离函数近的语法糖装饰器。

虽然变量的关系较为复杂,不过执行结果较为有规律。

  1. wrapper2(add_func2)中在被装饰函数之前的代码
  2. wrapper1(add_func1)中在被装饰函数之前的代码
  3. 被装饰函数代码
  4. wrapper1(add_func1)中在被装饰函数之后的代码
  5. wrapper2(add_func2)中在被装饰函数之后的代码

发表评论