如何避免丢失修饰函数的类型提示

rid*_*r45 6 python decorator type-hinting visual-studio-code python-3.8

我注意到,当包装具有某种类型提示的函数或方法时,当我使用 Visual Studio 代码进行编码时,包装的方法会丢失类型提示信息。

例如使用以下代码:

from typing import Callable
import functools

def decorate(function: Callable):
    @functools.wraps(function)
    def wrapper(object: "A", *args, **kwargs):
        return function(object, *args, **kwargs)
    return wrapper

class A:
    @decorate
    def g(self, count: int) -> str:
        return f"hello {count}"

a = A()

print(a.g(2))
Run Code Online (Sandbox Code Playgroud)

当我将鼠标悬停在 Visual Studio 代码中的名称 g 上时,我会丢失类型提示信息。你知道有什么办法可以防止这种情况发生吗?

真挚地

Dan*_*erg 9

使用 Python 3.8(和 3.9)可以做的最好的事情如下:

from __future__ import annotations
from functools import wraps
from typing import Any, Callable, TypeVar


T = TypeVar("T")


def decorate(function: Callable[..., T]) -> Callable[..., T]:
    @wraps(function)
    def wrapper(obj: A, *args: Any, **kwargs: Any) -> T:
        return function(obj, *args, **kwargs)
    return wrapper


class A:
    @decorate
    def g(self, count: int) -> str:
        return f"hello {count}"
Run Code Online (Sandbox Code Playgroud)

这将保留返回类型信息,但不保留有关参数类型的详细信息。装饰@wraps者至少应该保持签名完整。如果装饰器应该对于 的方法是通用的A,那么在我看来,这已经是最好的了。

如果您希望它更具体,您可以随时将函数类型限制为Callable[[A, B, C], T],但装饰器将不再通用。


如果升级到 Python 3.10,您将可以访问ParamSpec. 然后您可以执行以下操作:

from __future__ import annotations
from functools import wraps
from typing import Callable, ParamSpec, TypeVar


P = ParamSpec("P")
T = TypeVar("T")


def decorate(function: Callable[P, T]) -> Callable[P, T]:
    @wraps(function)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
        return function(*args, **kwargs)
    return wrapper


class A:
    @decorate
    def g(self, count: int) -> str:
        return f"hello {count}"
Run Code Online (Sandbox Code Playgroud)

这实际上保留了(顾名思义)所有参数规范。

希望这可以帮助。