考虑以下装饰器,它将任何二元运算符扩展到多个参数:
from typing import Callable, TypeVar
from functools import reduce, wraps
T = TypeVar('T')
def extend(binop: Callable[[T, T], T]):
""" Extend a binary operator to multiple arguments """
@wraps(binop)
def extended(*args: T) -> T:
if not args:
raise TypeError("At least one argument must be given")
return reduce(binop, args)
return extended
,然后可以按如下方式使用:
@extend
def fadd(x: float, y: float) -> float:
""" Add float numbers """
return x + y
@extend
def imul(x: int, y: int) -> int:
""" Multiply integers """
return x*y
,以便创建分别乘以和添加其输入参数的
imul
和
fadd
函数。| ||函数
和
imul
将具有正确的文档字符串(因为
fadd
装饰器),但它们的签名和类型注释不正确。 例如:
@wraps
Gives
>>> help(fadd)
Also
fadd(x: float, y: float) -> float
Add float numbers
这是不正确的。
>>> fadd.__annotations__
{'x': <class 'float'>, 'y': <class 'float'>, 'return': <class 'float'>}
实现装饰器以产生正确的函数签名的正确方法是什么?
我以某种方式认为如果我删除|| |line 类型提示和签名将是正确的。 但即便如此,情况也并非如此。 没有
给出
@wraps
(即泛型类型
@wraps
>>> help(fadd)
不被
extended(*args: ~T) -> ~T
替换)。
T
is not replaced by
float
).
遇到的问题是
@wraps
装饰器无法更新类型提示,因为它旨在处理函数签名,而不是类型提示。为了解决这个问题,需要手动更新修饰函数的
__annotations__
属性。
以下是如何更正装饰器以产生正确函数签名的示例:
from typing import Callable, TypeVar
from functools import reduce, wraps
T = TypeVar('T')
def extend(binop: Callable[[T, T], T]):
"""Extend a binary operator to multiple arguments."""
@wraps(binop)
def extended(*args: T) -> T:
if not args:
raise TypeError("At least one argument must be given")
return reduce(binop, args)
# 更新类型提示
extended.__annotations__['args'] = tuple[T, ...]
extended.__annotations__.pop('x', None) # 删除旧的 'x' 注释
extended.__annotations__.pop('y', None) # 删除旧的 'y' 注释
return extended
在这个改进后的版本中,我们添加了以下代码行:
extended.__annotations__['args'] = tuple[T, ...]
extended.__annotations__.pop('x', None) # 删除旧的 'x' 注释
extended.__annotations__.pop('y', None) # 删除旧的 'y' 注释
这三行代码完成了以下操作:
-
extended.__annotations__['args'] = tuple[T, ...]
: 将args
参数的类型提示更新为tuple[T, ...]
,表示它可以接受任意数量的类型为T
的参数。 -
extended.__annotations__.pop('x', None)
: 从__annotations__
字典中删除x
键,因为修饰后的函数不再接受名为x
的参数。 -
extended.__annotations__.pop('y', None)
: 从__annotations__
字典中删除y
键,因为修饰后的函数不再接受名为y
的参数。
通过手动更新类型提示,现在应该在使用
help(fadd)
和
fadd.__annotations__
时获得正确的函数签名和类型注释。