functools.wraps是作什么的?

对另外一个问题的答案发表评论时,有人说他们不肯定functools.wraps在作什么。 因此,我问这个问题,以便在StackOverflow上有它的记录以备未来参考: functools.wraps是什么? html


#1楼

我常常将类而不是函数用于装饰器。 我在这方面遇到了麻烦,由于对象不会具备函数所指望的全部相同属性。 例如,一个对象将没有属性__name__ 。 我有一个特定的问题,很难跟踪Django报告错误“对象没有属性' __name__ '”的位置。 不幸的是,对于类风格的装饰器,我认为@wrap不会胜任。 相反,我建立了一个基本的装饰器类,以下所示: python

class DecBase(object):
    func = None

    def __init__(self, func):
        self.__func = func

    def __getattribute__(self, name):
        if name == "func":
            return super(DecBase, self).__getattribute__(name)

        return self.func.__getattribute__(name)

    def __setattr__(self, name, value):
        if name == "func":
            return super(DecBase, self).__setattr__(name, value)

        return self.func.__setattr__(name, value)

此类将全部属性调用代理到要修饰的函数。 所以,您如今能够建立一个简单的装饰器来检查是否指定了2个参数,以下所示: git

class process_login(DecBase):
    def __call__(self, *args):
        if len(args) != 2:
            raise Exception("You can only specify two arguments")

        return self.func(*args)

#2楼

使用装饰器时,您将一个功能替换为另外一个功能。 换句话说,若是您有一个装饰器 github

def logged(func):
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging

而后当你说 app

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

这和说的彻底同样 函数

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

而且将函数f替换为with_logging函数。 不幸的是,这意味着若是您而后说 this

print(f.__name__)

它将显示with_logging由于那是新函数的名称。 实际上,若是您查看f的文档字符串,它将为空,由于with_logging没有文档字符串,所以您编写的文档字符串将再也不存在。 另外,若是您查看该函数的pydoc结果,它将不会被列为采用一个参数x ; 相反,它将被列为带有*args**kwargs由于with_logging须要这样作。 spa

若是使用装饰器老是意味着丢失有关功能的信息,那将是一个严重的问题。 这就是为何咱们有functools.wraps的缘由。 它采用了装饰器中使用的功能,并添加了复制功能名称,文档字符串,参数列表等的功能。因为wraps自己就是装饰器,所以如下代码能够正确执行操做: 代理

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

print(f.__name__)  # prints 'f'
print(f.__doc__)   # prints 'does some math'

#3楼

  1. 先决条件:您必须知道如何使用装饰器,尤为是包装器。 该评论对其进行了清晰解释,或者该连接也对其进行了很好的解释。 code

  2. 每当咱们使用For例如:@wraps后跟咱们本身的包装函数。 根据此连接中给出的详细信息,它表示

functools.wraps是方便函数,用于在定义包装函数时调用update_wrapper()做为函数装饰器。

它等效于partial(update_wrapper,wraped = wrapped,assigned = assigned,updated = updated)。

所以,@ wraps装饰器实际上会调用functools.partial(func [,* args] [,** keywords])。

functools.partial()定义说

partial()用于部分函数应用程序,该函数“冻结”函数的参数和/或关键字的某些部分,从而生成具备简化签名的新对象。 例如,partial()可用于建立行为相似于int()函数的可调用对象,其中基本参数默认为两个:

>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18

这使我得出如下结论:@wraps调用了partial(),并将包装函数做为参数传递给它。 最后,partial()返回简化版本,即包装函数内部的对象,而不是包装函数自己。


#4楼

简而言之, functools.wraps只是一个常规函数。 让咱们考虑这个官方示例 。 借助于源代码 ,咱们能够看到有关实现和运行步骤的更多详细信息,以下所示:

  1. wraps(f)返回一个对象,例如O1 。 它是Partial类的对象
  2. 下一步是@ O1 ... ,这是python中的修饰符符号。 它的意思是

wrapper = O1 .__ call __(wrapper)

检查的执行__call__ ,咱们看到这一步,(左侧) 包装成为对象形成的self.func(* self.args,* ARGS,** newkeywords)检查在__new__ O1的建立后,咱们知道self.func是功能update_wrapper。 它使用参数* args (右侧包装器 )做为其第一个参数。 检查update_wrapper的最后一步,能够看到返回了右侧包装器 ,并根据须要修改了一些属性。


#5楼

这是关于包装的源代码:

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')

WRAPPER_UPDATES = ('__dict__',)

def update_wrapper(wrapper,
                   wrapped,
                   assigned = WRAPPER_ASSIGNMENTS,
                   updated = WRAPPER_UPDATES):

    """Update a wrapper function to look like the wrapped function

       wrapper is the function to be updated
       wrapped is the original function
       assigned is a tuple naming the attributes assigned directly
       from the wrapped function to the wrapper function (defaults to
       functools.WRAPPER_ASSIGNMENTS)
       updated is a tuple naming the attributes of the wrapper that
       are updated with the corresponding attribute from the wrapped
       function (defaults to functools.WRAPPER_UPDATES)
    """
    for attr in assigned:
        setattr(wrapper, attr, getattr(wrapped, attr))
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    # Return the wrapper so this can be used as a decorator via partial()
    return wrapper

def wraps(wrapped,
          assigned = WRAPPER_ASSIGNMENTS,
          updated = WRAPPER_UPDATES):
    """Decorator factory to apply update_wrapper() to a wrapper function

   Returns a decorator that invokes update_wrapper() with the decorated
   function as the wrapper argument and the arguments to wraps() as the
   remaining arguments. Default arguments are as for update_wrapper().
   This is a convenience function to simplify applying partial() to
   update_wrapper().
    """
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)
相关文章
相关标签/搜索