可以在没有参数的情况下使用的打字装饰器

发布于 2025-02-01 18:06:15 字数 1720 浏览 5 评论 0 原文

我有一个可以在没有或有争论的情况下打电话给的装饰器(所有字符串):

@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

我很难提供适当的类型提示,因此,尽管阅读了 mypy of mypy 的相关部分。

这是我到目前为止尝试过的:

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

我正在使用Python 3.10.4 ,mypy 0.960,Pyright 1.1.249。

I have a decorator that can be called either without or with arguments (all strings):

@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

I am having a hard time providing appropriate type hints so that type checkers will be able to properly validate the usage of the decorator, despite having read the related section of the doc of mypy.

Here is what I have tried so far:

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

Which results in the following error from mypy:

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

I also have an error with 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

剧终人散尽 2025-02-08 18:06:15

问题来自第一个过载(我应该读取 Pyright 消息两次!):

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

此过载接受一个关键字参数,名为 arg arg ,而实现则没有!

当然,对于 @decorator 符号使用的装饰器而言,这当然无关紧要,但是如果它被称为So: fct2 = Decorator(arg = fct)> 。

python> = 3.8

解决问题的最佳方法是更改​​第一个过载,以便 arg a 仅位置参数(因此不能用作关键字参数):

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

支持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

The issue comes from the first overload (I should have read the pyright message twice!):

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

This overload accepts a keyword parameter named arg, while the implementation does not!

Of course this does not matter in the case of a decorator used with the @decorator notation, but could if it is called like so: fct2 = decorator(arg=fct).

Python >= 3.8

The best way to solve the issue would be to change the first overload so that arg is a positional-only parameter (so cannot be used as a keyword argument):

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

With support for Python < 3.8

Since positional-only parameters come with Python 3.8, we cannot change the first overload as desired.

Instead, let's change the implementation to allow for a **kwargs parameter (an other possibility would be to add a keyword arg parameter). But now we need to handle it properly in the code implementation, for example:

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

    # rest of the implementation here
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文