我有一个decorator,可以无参数调用,也可以有参数调用(所有字符串):

@decorator
def fct0(a: int, b: int) -> int:
    return a * b


@decorator("foo", "bar")  # any number of arguments
def fct1(a: int, b: int) -> int:
    return a * b

我很难提供适当的类型提示,以便类型判断器能够正确验证decorator的使用,尽管已经阅读了related section of the doc of mypy.

以下是我迄今为止所做的try :

from typing import overload, TypeVar, Any, Callable

F = TypeVar("F", bound=Callable[..., Any])

@overload
def decorator(arg: F) -> F:
    ...

@overload
def decorator(*args: str) -> Callable[[F], F]:
    ...

def decorator(*args: Any) -> Any:
    # python code adapted from https://stackoverflow.com/q/653368

    # @decorator -> shorthand for @decorator()
    if len(args) == 1 and callable(args[0]):
        return decorator()(args[0])

    # @decorator(...) -> real implementation
    def wrapper(fct: F) -> F:
        # real code using `args` and `fct` here redacted for clarity
        return fct

    return wrapper

这将导致以下mypy的误差:

error: Overloaded function implementation does not accept all possible arguments of signature 1

我还有一个pyright的错误:

error: Overloaded implementation is not consistent with signature of overload 1
  Type "(*args: Any) -> Any" cannot be assigned to type "(arg: F@decorator) -> F@decorator"
    Keyword parameter "arg" is missing in source

I am using python 3.10.4, mypy 0.960, pyright 1.1.249.

推荐答案

问题来自第一次过载(我应该把pyright条消息读两遍!):

@overload
def decorator(arg: F) -> F:
    ...

此重载接受名为arg的关键字参数,而实现不接受!

当然,对于与@decorator符号一起使用的decorator 来说,这并不重要,但如果它被称为fct2 = decorator(arg=fct),则可能会这样.

Python >= 3.8

解决此问题的最佳方法是更改第一个重载,使argpositional-only parameter(因此不能用作关键字参数):

@overload
def decorator(arg: F, /) -> F:
    ...

With support for Python < 3.8

由于Python 3.8附带了仅位置参数,因此我们无法根据需要更改第一个重载.

相反,让我们更改实现以允许使用**kwargs参数(另一种可能是添加关键字arg参数).但现在我们需要在代码实现中正确处理它,例如:

def decorator(*args: Any, **kwargs: Any) -> Any:
    if kwargs:
        raise TypeError("Unexpected keyword argument")

    # rest of the implementation here

Python相关问答推荐

Python多处理:当我在一个巨大的pandas数据框架上启动许多进程时,程序就会陷入困境

对某些列的总数进行民意调查,但不单独列出每列

抓取rotowire MLB球员新闻并使用Python形成表格

查找两极rame中组之间的所有差异

如何在Windows上用Python提取名称中带有逗号的文件?

如何找到满足各组口罩条件的第一行?

在Polars(Python库)中将二进制转换为具有非UTF-8字符的字符串变量

Python库:可选地支持numpy类型,而不依赖于numpy

计算组中唯一值的数量

多指标不同顺序串联大Pandas 模型

python panda ExcelWriter切换动态公式到数组公式

在pandas/python中计数嵌套类别

Python—转换日期:价目表到新行

如何获取Python synsets列表的第一个内容?

在numpy数组中寻找楼梯状 struct

Pandas数据框上的滚动平均值,其中平均值的中心基于另一数据框的时间

高效生成累积式三角矩阵

随机森林n_估计器的计算

如何将一个文件的多列导入到Python中的同一数组中?

为什么按下按钮后屏幕的 colored颜色 保持不变?