如何输入提示函数,通过Python中的类装饰器添加到类中

che*_*sun 11 dynamic type-hinting python-3.x python-decorators python-typing

我有一个类装饰器,它向装饰类添加了一些函数和字段。

@mydecorator
@dataclass
class A:
    a: str = ""
Run Code Online (Sandbox Code Playgroud)

添加(通过setattr())是一个.save()函数和一组数据类字段的信息作为单独的字典。

我希望 VScode 和 mypy 能够正确识别这一点,以便当我使用时:

a=A()
a.save()
Run Code Online (Sandbox Code Playgroud)

或者a.my_fields_dict这两个被正确识别。

有什么办法可以做到这一点吗?也许A在运行时修改类类型注释?

Dan*_*erg 13

长话短说

当前的类型系统无法实现您想要做的事情。


1. 交叉口类型

如果您通过装饰器添加到类中的属性和方法是静态的(从某种意义上说,它们不仅仅在运行时已知),那么您所描述的实际上是通过混合协议来扩展任何给T P。该协议定义了方法save等等。

要对此进行注释,您需要一个的交集T & P。它看起来像这样:

from typing import Protocol, TypeVar


T = TypeVar("T")


class P(Protocol):
    @staticmethod
    def bar() -> str: ...


def dec(cls: type[T]) -> type[Intersection[T, P]]:
    setattr(cls, "bar", lambda: "x")
    return cls  # type: ignore[return-value]


@dec
class A:
    @staticmethod
    def foo() -> int:
        return 1
Run Code Online (Sandbox Code Playgroud)

Intersection您可能会注意到明显缺少导入。这是因为,尽管它是 Python 类型系统最受欢迎的功能之一,但迄今为止它仍然缺失。目前还没有办法用 Python 类型来表达这个概念。


2.类装饰器问题

目前唯一的解决方法是自定义实现以及您选择的类型检查器的相应插件。我刚刚偶然发现了这个typing-protocol-intersection包,它就是为mypy.

如果您安装它并添加plugins = typing_protocol_intersection.mypy_plugin到您的mypy配置中,您可以像这样编写代码:

from typing import Protocol, TypeVar

from typing_protocol_intersection import ProtocolIntersection


T = TypeVar("T")


class P(Protocol):
    @staticmethod
    def bar() -> str: ...


def dec(cls: type[T]) -> type[ProtocolIntersection[T, P]]:
    setattr(cls, "bar", lambda: "x")
    return cls  # type: ignore[return-value]


@dec
class A:
    @staticmethod
    def foo() -> int:
        return 1
Run Code Online (Sandbox Code Playgroud)

但在这里我们遇到了下一个问题。使用reveal_type(A.bar())via进行测试mypy将产生以下结果:

error: "Type[A]" has no attribute "bar"  [attr-defined]
note: Revealed type is "Any"
Run Code Online (Sandbox Code Playgroud)

但如果我们这样做:

error: "Type[A]" has no attribute "bar"  [attr-defined]
note: Revealed type is "Any"
Run Code Online (Sandbox Code Playgroud)

mypy我们没有收到和的投诉note: Revealed type is "builtins.str"。尽管我们之前所做的是等效的!

这不是插件的错误,而是mypy内部的错误。这是另一个长期存在的问题,它mypy不能正确处理类装饰器。

该问题线程中的一个人甚至提到了您的用例以及所需的交叉点类型。


DIY

换句话说,你只需要等待这两个漏洞被修补即可。或者您可以希望至少装饰器问题mypy很快得到解决,同时为交叉类型编写您自己的 VSCode 插件。也许你可以和我上面提到的那个插件背后的人聚在一起mypy