python笔记36-装饰器之wraps

前言

前面一篇对python装饰器有了初步的了解了,但是还不够完美,领导看了后又提出了新的需求,希望运行的日志能显示出具体运行的哪个函数。

name__和__doc

__name__用于获取函数的名称,__doc__用于获取函数的docstring内容(函数的注释)

import time

def func_a(a):
    '''func_a --> hello'''
    print("hello"+a)
    time.sleep(0.5)
    return True


def func_b(b, c="xx"):
    '''func_b --> world'''
    print("world"+b+c)
    time.sleep(0.8)
    return True

if __name__ == '__main__':
    print(func_a.__name__)  # 结果 func_a
    print(func_a.__doc__)   # func_a --> hello
    print(func_b.__name__)  # func_b
    print(func_b.__doc__)   # func_b --> world
装饰器加函数名称日志

在装饰器里面添加2行代码,打印正在运行函数的名称和docstring内容

import time


def runtime(func):
    '''runtime decorators'''
    def wrapper(*args, **kwargs):
        '''wrapper inner fuction'''
        print("running function : %s" % func.__name__)   
        print("docstring: %s" % func.__doc__)
        start = time.time()
        f = func(*args, **kwargs)     # 原函数
        end = time.time()
        print("运行时长:%.4f 秒" % (end-start))
        return f
    return wrapper


@runtime
def func_a(a):
    '''func_a --> hello'''
    print("hello"+a)
    time.sleep(0.5)
    return True


@runtime
def func_b(b, c="xx"):
    '''func_b --> world'''
    print("world"+b+c)
    time.sleep(0.8)
    return True

if __name__ == '__main__':
    func_a("a")
    print(func_a.__name__)
    print(func_a.__doc__)

运行结果

running function : func_a
docstring: func_a --> hello
helloa
运行时长:0.5008 秒
wrapper
wrapper inner fuction

从运行的结果可以看出,func_a.__name__运行的结果是wrapper, func_a.__doc__运行的结果是wrapper inner fuction。
也就是说被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),那这个问题如何解决呢?
这就需要用到functools里面的一个wraps函数了

functools

当func_a函数被装饰后,导致了一个副作用:自身的函数属性和docstring内容变成了wrapper函数的属性了。
这里需用到functools里面的一个wraps的装饰器来消除这样的副作用。

import time
from functools import wraps

def runtime(func):
    '''runtime decorators'''
    @wraps(func)
    def wrapper(*args, **kwargs):
        '''wrapper inner fuction'''
        print("running function : %s" % func.__name__)
        print("docstring: %s" % func.__doc__)
        start = time.time()
        f = func(*args, **kwargs)     # 原函数
        end = time.time()
        print("运行时长:%.4f 秒" % (end-start))
        return f
    return wrapper

只需在wrapper函数上加上@wraps(func)即可解决

运行结果

running function : func_a
docstring: func_a --> hello
helloa
运行时长:0.5004 秒
func_a
func_a --> hello
类装饰器

带参数的装饰器,可以写成类装饰器

import time
from functools import wraps

class runtime(object):
    '''runtime class decorators'''
    def __init__(self, slowly=1):
        self.slowly = slowly

    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            '''wrapper inner fuction'''
            print("running function : %s" % func.__name__)
            print("docstring: %s" % func.__doc__)
            start = time.time()
            f = func(*args, **kwargs)     # 原函数
            end = time.time()
            t = end-start
            time.sleep((self.slowly-1)*t)  # 延迟效果
            new_end = time.time()
            print("运行时长:%.4f 秒" % (new_end-start))
            return f
        return wrapper


@runtime(1.5)
def func_a(a):
    '''func_a --> hello'''
    print("hello"+a)
    time.sleep(0.5)
    return True


@runtime()
def func_b(b, c="xx"):
    '''func_b --> world'''
    print("world"+b+c)
    time.sleep(0.8)
    return True

if __name__ == '__main__':
    func_a("a")
    print(func_a.__name__)
    print(func_a.__doc__)

运行结果

running function : func_a
docstring: func_a --> hello
helloa
运行时长:0.7522 秒
func_a
func_a --> hello

python自动化交流 QQ群:779429633

上一篇:@wraps 修饰器:让你的 Python 代码更加简短可爱 | 从简单实例来认识它


下一篇:51单片机学习笔记——数字秒表设计(普中51中级实验例程)