【问题标题】:How to type hint a function, added to class by class decorator in Python如何键入提示函数,在 Python 中通过类装饰器添加到类中
【发布时间】:2023-01-13 04:21:47
【问题描述】:

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

@mydecorator
@dataclass
class A:
    a: str = ""

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

我希望 VScode 和 mypy 正确识别它,以便在我使用时:

a=A()
a.save()

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

有什么办法吗?也许在运行时修改类 A 类型注释?

【问题讨论】:

  • 想想你在说什么。你想更改注释运行,但是静止的类型检查器了解它们?静态类型检查器不执行您的代码,它们只是读取代码。

标签: python-3.x dynamic python-decorators type-hinting python-typing


【解决方案1】:

长话短说

当前类型系统无法执行您尝试执行的操作。


1.路口类型

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

您可能会注意到 Intersection 的导入明显缺失。这是因为尽管它是 Python 类型系统的 most requested features 之一,但直到今天它仍然缺失。目前还没有办法在 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

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

error: "Type[A]" has no attribute "bar"  [attr-defined]
note: Revealed type is "Any"

然而,如果我们这样做:

class A:
    @staticmethod
    def foo() -> int:
        return 1


B = dec(A)

reveal_type(B.bar())

我们没有收到来自mypynote: Revealed type is "builtins.str" 的投诉。虽然我们之前做的是等价的!

这不是插件的错误,而是mypy 内部的错误。这是另一个long-standing issuemypy 没有正确处理类装饰器。


DIY

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-18
    • 2011-07-25
    • 1970-01-01
    • 1970-01-01
    • 2018-06-05
    • 2021-12-04
    • 2019-09-24
    • 2012-04-11
    相关资源
    最近更新 更多