📜  Python Functools – update_wrapper()

📅  最后修改于: 2022-05-13 01:55:43.505000             🧑  作者: Mango

Python Functools – update_wrapper()

Python中的functools模块处理高阶函数,即操作(作为参数)或返回函数和其他此类可调用对象的函数。 functools 模块提供了大量的方法,例如cached_property(func), cmp_to_key(func), lru_cache(func), wraps(func)等。值得注意的是,这些方法以函数作为参数。

在本文中,我们将讨论 functools 模块提供的update_wrapper()方法的用途和应用。此方法用于更新包装函数的元数据以反映被包装函数的元数据,从而提高代码的可读性和可重用性。 update_wrapper()方法采用以下参数:

为了更好地理解这种方法,让我们观察一些在Python中使用装饰器和局部的案例。

示例 1:

# Defining the decorator
def hi(func):
  
    def wrapper():
        "Hi has taken over Hello Documentation"
        print("Hi geeks")
        func()
  
    return wrapper
      
@hi
def hello():
  
    "this is the documentation of Hello Function"
    print("Hey Geeks")
  
# Driver Code
print(hello.__name__)
print(hello.__doc__)
help(hello)

输出:

wrapper
Hi has taken over Hello Documentation
Help on function wrapper in module __main__:

wrapper()
    Hi has taken over Hello Documentation

在上面的例子中,当我们使用装饰器函数hi并使用它的包装器来包装hello时,函数hello模块级常量,例如__name____doc__等,被替换为函数hi中的包装器

使用 functools 模块中的 partials 时也会出现同样的情况。让我们看一个例子:

示例 2:

import functools
  
  
def divide(a, b):
    "Original Documentation of Divide"
    return a / b
  
half = functools.partial(divide, b = 2)
oneThird = functools.partial(divide, b = 3)
  
try:
    print(half.__name__)
except AttributeError:
    print('No Name')
print(half.__doc__)

输出:

在上面的示例中,half 和 oneThird 没有__name__ (引用时抛出 AttributeError ),因为它们是通过部分创建的。作为他们的文档,他们继承了部分方法的文档。

上述场景本质上是有问题的,因为模块级常量用于识别、管理和检索内容的重要目的。由于这些原因以及跟踪内容的使用情况,元数据非常重要。因此,如果尝试创建 API 或库,它不会对用户友好,因为help(函数)不会返回有关如何使用其方法的有意义的信息。 help(函数)将返回包装函数的文档,这会使用户感到困惑。这个问题可以通过@functools.update_wrapper() 轻松解决。

让我们考虑第一个例子。我们可以通过以下方式使用 update_wrapper():
示例 1:

# Python program to demonstrate
# ipdate)wrapper() method
  
  
import functools as ft
  
  
# Defining the decorator
def hi(func):
      
    def wrapper():
        "Hi has taken over Hello Documentation"
        print("Hi geeks")
        func()
          
    # Note The following Steps Clearly
    print("UPDATED WRAPPER DATA")
    print(f'WRAPPER ASSIGNMENTS : {ft.WRAPPER_ASSIGNMENTS}')
    print(f'UPDATES : {ft.WRAPPER_UPDATES}')
      
    # Updating Metadata of wrapper 
    # using update_wrapper
    ft.update_wrapper(wrapper, func)
    return wrapper
      
@hi
def hello():
    "this is the documentation of Hello Function"
    print("Hey Geeks")
  
print(hello.__name__)
print(hello.__doc__)
help(hello)

输出:

通过使用 update_wrapper() 我们看到函数hello 保留了它的原始元数据。同样,让我们检查第二个示例,但这次我们将使用 update_wrapper()

# Python program to demonstrate
# ipdate)wrapper() method
  
  
import functools
  
  
def divide(a, b):
    "Original Documentation of Divide"
    return a / b
  
half = functools.partial(divide, 
                         b = 2)
  
oneThird = functools.partial(divide, 
                             b = 3)
  
print("UPDATED WRAPPER DATA")
print(f'WRAPPER ASSIGNMENTS : {functools.WRAPPER_ASSIGNMENTS}')
print(f'UPDATES : {functools.WRAPPER_UPDATES}')
  
# Updating Metadata of wrapper
# using update_wrapper
ft.update_wrapper(half, divide)
  
try:
    print(half.__name__)
      
except AttributeError:
    print('No Name')
      
print(half.__doc__)
  
help(half)
help(oneThird)

输出:

在这个例子中,我们看到 half 继承了函数divide 的基本模块级常量。当我们使用 help(half) 时,我们看到它部分来自于除法。值得注意的是 oneThird 并非如此,因为 help(oneThird) 没有告诉我们父函数。

因此,通过上面的图解,我们可以了解functools模块中提供的update_wrapper(…)方法的用例。为了保存函数的元数据以供进一步使用,update_wrapper(…) 是一个强大的工具,可以毫不费力地使用。因此,该方法在decorators 和 partials的情况下被证明是非常有用的。