python-3.x - 如何更正装饰函数签名和类型提示?
问题描述
考虑以下将任何二元运算符扩展到多个参数的装饰器:
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
装饰器),但它们的签名和类型注释不正确。例如:
>>> help(fadd)
给
fadd(x: float, y: float) -> float
Add float numbers
还
>>> fadd.__annotations__
{'x': <class 'float'>, 'y': <class 'float'>, 'return': <class 'float'>}
这是不正确的。
实现装饰器以产生正确的函数签名的正确方法是什么?
我不知何故认为,如果我删除@wraps
行,类型提示和签名将是正确的。但即便如此,也并非如此。没有@wraps
>>> help(fadd)
给
extended(*args: ~T) -> ~T
(即,泛型类型T
不被替换float
)。
解决方案
我使用inspect
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)
sig = inspect.signature(extended)
sig = sig.replace(
parameters=[inspect.Parameter('args', inspect.Parameter.VAR_POSITIONAL,
annotation=sig.return_annotation)]
)
extended.__signature__ = sig
return extended
它不像我预期的那么优雅,特别是因为它在很大程度上取决于binop
返回值必须与其参数具有相同类型的事实(否则注释将是错误的)。
然而,我很高兴知道更好的解决方案。
推荐阅读
- ios - 制作用于调试视图层次结构的 Xcode 行为
- cookies - 注销时如何通过自定义 UI 删除在 AD B2C 中添加的自定义 cookie
- sdk - sdk-3.1.1.js:1 未捕获类型错误:无法在“URL”上执行“createObjectURL”:未找到与提供的签名匹配的函数
- json - 使用 Jest 测试本地 JSON 文件?
- python - 我在 MapView 中遇到 MapMarker 问题
- excel - 类似措辞的 Cells.Replace 函数
- json - 使用 React 从公共 json 文件而不是 REST api 获取 json 数据
- autohotkey - AHK WinActivate 无法排除
- google-calendar-api - 开始从 Google Calendar API 接收 429 Too Many Requests
- html - CSS 动画:`backface-visibility` 导致跨浏览器问题?