如何注释函数生成数据类?

Дан*_*сов 6 python python-3.x mypy python-typing

dataclass假设你想像这样包装装饰器:

from dataclasses import dataclass

def something_else(klass):
    return klass

def my_dataclass(klass):
    return something_else(dataclass(klass))
Run Code Online (Sandbox Code Playgroud)

应该如何my_dataclass和/或something_else注释以指示返回类型是数据类?请参阅以下示例,了解内置函数如何@dataclass工作但自定义函数@my_dataclass则不然:


@dataclass
class TestA:
    a: int
    b: str

TestA(0, "") # fine


@my_dataclass
class TestB:
    a: int
    b: str

TestB(0, "") # error: Too many arguments for "TestB" (from mypy)
Run Code Online (Sandbox Code Playgroud)

Mis*_*agi 7

在PEP 681之前没有可行的方法来做到这一点。

\n

Adataclass描述的不是类型而是转换。其实际效果无法通过 Python 的类型系统 \xe2\x80\x93 来表达,它由MyPy 插件@dataclass处理,该插件检查代码,而不仅仅是类型。这是在特定装饰器上触发的,而不了解它们的实现。

\n
dataclass_makers: Final = {\n    \'dataclass\',\n    \'dataclasses.dataclass\',\n}\n
Run Code Online (Sandbox Code Playgroud)\n

虽然可以提供自定义 MyPy 插件,但这通常超出了大多数项目的范围。PEP 681 (Python 3.11) 添加了一个通用的“此装饰器的行为类似于@dataclass”标记,可用于从注释到字段的所有转换器。

\n

PEP 681 可通过typing_extensions.

\n

强制执行数据类

\n

对于纯类型替代方案,定义自定义装饰器以获取数据类并修改它。数据类可以通过其字段来标识__dataclass_fields__

\n
from typing import Protocol, Any, TypeVar, Type\nimport dataclasses\n\nclass DataClass(Protocol):\n    __dataclass_fields__: dict[str, Any]\n\nDC = TypeVar("DC", bound=DataClass)\n\ndef my_dataclass(klass: Type[DC]) -> Type[DC]:\n    ...\n
Run Code Online (Sandbox Code Playgroud)\n

这使得类型检查器能够理解并验证dataclass是否需要一个类。

\n
@my_dataclass\n@dataclass\nclass TestB:\n    a: int\n    b: str\n\nTestB(0, "")  # note: Revealed type is "so_test.TestB"\n\n@my_dataclass\nclass TestC:  # error: Value of type variable "DC" of "my_dataclass" cannot be "TestC"\n    a: int\n    b: str\n
Run Code Online (Sandbox Code Playgroud)\n

自定义类似数据类的装饰器

\n

PEP 681装饰器是其他dataclass_transform装饰器的标记,以表明它们的行为“相似” 。为了匹配 的行为,必须使用来指示字段以相同的方式表示。@dataclass@dataclassfield_specifiers

\n
from typing import dataclass_transform, TypeVar, Type\nimport dataclasses\n\nT = TypeVar("T")\n\n@dataclass_transform(\n    field_specifiers=(dataclasses.Field, dataclasses.field),\n)\ndef my_dataclass(klass: Type[T]) -> Type[T]:\n    return something_else(dataclasses.dataclass(klass))\n
Run Code Online (Sandbox Code Playgroud)\n

自定义数据类装饰器可以将所有关键字视为@dataclassdataclass_transform可以用来标记它们各自的默认值,即使装饰器本身不接受作为关键字。

\n