平凡

python的@functools.wraps

2018/10/03 Share

前言

如果你经常与python的装饰器打交道,会发现有的代码里经常会有这样一行@wraps(xxx)

from functools import wraps
def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
   """does some math"""
   return x + x * x

这是为什么呢?

不用wraps带来的问题

当你使用python的自省机制来检查别人的代码,被装饰器装饰的函数会返回错误的信息,例如:

def my_decorator(func):
    # @functools.wraps(func)
    def inner(*args, **kwargs):
        """I am the inner funciton"""
        print("*****")
        func(*args, **kwargs)
        print("***********")

    return inner


@my_decorator
def add(a, b):
    """this is the add function"""
    print(a, b, a + b)


print(add.__doc__)
print(add.__name__)

想了解__doc____name__具体细节的可以查阅相关资料。简单来说,__doc__就是函数下面"""xxxx"""的注释内容;__name__就是函数的名称。

理论上我们想要的结果是:

this is the add function
add

但实际上的输出却是:

I am the inner function
inner

原因如下:

上面的代码,我们用my_decorator来装饰add函数,这个装饰器把原来函数的__doc____name__都更改了

wraps拯救大法

如何让上面代码输出我们理想的结果?答案是functions.wraps!

我们仅仅把上面代码中的注释那行去掉:

import functools


def my_decorator(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        """I am the inner funciton"""
        print("*****")
        func(*args, **kwargs)
        print("***********")

    return inner


@my_decorator
def add(a, b):
    """this is the add function"""
    print(a, b, a + b)


print(add.__doc__)
print(add.__name__)

结果就是我们想的内容了:

this is the add function
add

总结

wraps是一个很实用的方法,当你发现函数的__name____doc__不对时,你就应该懂得如何修补这个问题了。

如果不使用wraps,代码执行结果会出错吗?

答案是不会。是否使用wraps,对代码执行的顺序及代码执行的各种细节都不会有影响。

参考

另推荐stackoverflow上另一个非常友好的回答:

https://stackoverflow.com/questions/308999/what-does-functools-wraps-do

发表日期: October 3rd 2018

版权声明: 本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG
  1. 1. 前言
  2. 2. 不用wraps带来的问题
  3. 3. wraps拯救大法
  4. 4. 总结