Python Functools – update_wrapper()
Python中的functools模块处理高阶函数,即操作(作为参数)或返回函数和其他此类可调用对象的函数。 functools 模块提供了大量的方法,例如cached_property(func), cmp_to_key(func), lru_cache(func), wraps(func)
等。值得注意的是,这些方法以函数作为参数。
在本文中,我们将讨论 functools 模块提供的update_wrapper()
方法的用途和应用。此方法用于更新包装函数的元数据以反映被包装函数的元数据,从而提高代码的可读性和可重用性。 update_wrapper()
方法采用以下参数:
Syntax:@functools.update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES)
Parameters:
- wrapper: A wrapper function.
- wrapped: The function being wrapped over or the wrapped function.
- assigned: Attributes of the wrapped function which are assigned to the matching attributes of wrapper function as a tuple(optional argument).
- updated: Attributes of the wrapper function that are updated with respect to original function attributes as a tuple(optional argument).
为了更好地理解这种方法,让我们观察一些在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__)
输出:
No Name
partial(func, *args, **keywords) – new function with partial application
of the given arguments and keywords.
在上面的示例中,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)
输出:
UPDATED WRAPPER DATA
WRAPPER ASSIGNMENTS : (‘__module__’, ‘__name__’, ‘__qualname__’, ‘__doc__’, ‘__annotations__’)
UPDATES : (‘__dict__’, )
hello
this is the documentation of Hello Function
Help on function hello in module __main__:
hello()
this is the documentation of Hello Function
通过使用 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)
输出:
UPDATED WRAPPER DATA
WRAPPER ASSIGNMENTS : (‘__module__’, ‘__name__’, ‘__qualname__’, ‘__doc__’, ‘__annotations__’)
UPDATES : (‘__dict__’,)
divide
Original Documentation of Divide
Help on partial in module __main__ object:
divide = class partial(builtins.object)
| partial(func, *args, **keywords) – new function with partial application
| of the given arguments and keywords.
|
| Methods defined here:
|
| __call__(self, /, *args, **kwargs)
| Call self as a function.
|
| __delattr__(self, name, /)
| Implement delattr(self, name).
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| __reduce__(…)
| helper for pickle
|
| __repr__(self, /)
| Return repr(self).
|
| __setattr__(self, name, value, /)
| Implement setattr(self, name, value).
|
| __setstate__(…)
|
| ———————————————————————-
| Data descriptors defined here:
|
| __dict__
|
| args
| tuple of arguments to future partial calls
|
| func
| function object to use in future partial calls
|
| keywords
| dictionary of keyword arguments to future partial calls
Help on partial object:
class partial(builtins.object)
| partial(func, *args, **keywords) – new function with partial application
| of the given arguments and keywords.
|
| Methods defined here:
|
| __call__(self, /, *args, **kwargs)
| Call self as a function.
|
| __delattr__(self, name, /)
| Implement delattr(self, name).
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| __reduce__(…)
| helper for pickle
|
| __repr__(self, /)
| Return repr(self).
|
| __setattr__(self, name, value, /)
| Implement setattr(self, name, value).
|
| __setstate__(…)
|
| ———————————————————————-
| Data descriptors defined here:
|
| __dict__
|
| args
| tuple of arguments to future partial calls
|
| func
| function object to use in future partial calls
|
| keywords
| dictionary of keyword arguments to future partial calls
在这个例子中,我们看到 half 继承了函数divide 的基本模块级常量。当我们使用 help(half) 时,我们看到它部分来自于除法。值得注意的是 oneThird 并非如此,因为 help(oneThird) 没有告诉我们父函数。
因此,通过上面的图解,我们可以了解functools模块中提供的update_wrapper(…)方法的用例。为了保存函数的元数据以供进一步使用,update_wrapper(…) 是一个强大的工具,可以毫不费力地使用。因此,该方法在decorators 和 partials的情况下被证明是非常有用的。