如何注释一个函数产生一个数据类?
How to annotate that a function produces a dataclass?
假设你想像这样包装 dataclass
装饰器:
from dataclasses import dataclass
def something_else(klass):
return klass
def my_dataclass(klass):
return something_else(dataclass(klass))
my_dataclass
and/or something_else
应该如何注解表明return类型是数据类?
请参阅以下示例,了解内置 @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)
问题是 mypy 理解 metaclass decorator
及其魔力
关于__init__
,但是不明白 dataclass function
:
@dataclass
class Test:
a: int
# Mypy: Revealed type is "def (self: Test, a: builtins.int)"
reveal_type(Test.__init__)
class Test2:
a: int
#Mypy: Revealed type is "def (self: builtins.object)"
reveal_type(dataclass(Test2).__init__)
如您所见,Test2
的 __init__
方法不接受任何参数。
在 PEP 681 之前没有可行的方法来做到这一点。
A dataclass
描述的不是类型而是转换。 Python 的类型系统无法表达其实际效果 – @dataclass
由 MyPy Plugin which inspects the code, not just the types. This is triggered on specific decorators 在不了解其实现的情况下处理。
dataclass_makers: Final = {
'dataclass',
'dataclasses.dataclass',
}
虽然可以提供自定义 MyPy 插件,但这通常超出了大多数项目的范围。 PEP 681 (Python 3.11) 添加了一个通用的“这个装饰器的行为类似于 @dataclass
”-标记,可用于从注释到字段的所有转换器。
PEP 681 可通过 typing_extensions
.
用于早期 Python 版本
执行数据classes
对于纯类型替代方案,定义您的自定义装饰器以获取数据class并修改它。一个数据class可以通过它的__dataclass_fields__
字段来识别。
from typing import Protocol, Any, TypeVar, Type
import dataclasses
class DataClass(Protocol):
__dataclass_fields__: dict[str, Any]
DC = TypeVar("DC", bound=DataClass)
def my_dataclass(klass: Type[DC]) -> Type[DC]:
...
这允许类型检查器理解并验证需要dataclass
class。
@my_dataclass
@dataclass
class TestB:
a: int
b: str
TestB(0, "") # note: Revealed type is "so_test.TestB"
@my_dataclass
class TestC: # error: Value of type variable "DC" of "my_dataclass" cannot be "TestC"
a: int
b: str
自定义 dataclass-like 装饰器
PEP 681 dataclass_transform
装饰器是 其他 装饰器的标记,表明它们的行为“像”@dataclass
。为了匹配 @dataclass
的行为,必须使用 field_specifiers
来指示字段以相同的方式表示。
from typing import dataclass_transform, TypeVar, Type
import dataclasses
T = TypeVar("T")
@dataclass_transform(
field_specifiers=(dataclasses.Field, dataclasses.field),
)
def my_dataclass(klass: Type[T]) -> Type[T]:
return something_else(dataclasses.dataclass(klass))
自定义数据class装饰器可以将所有关键字都作为@dataclass
。 dataclass_transform
可用于标记它们各自的默认值,即使装饰器本身不接受为关键字。
假设你想像这样包装 dataclass
装饰器:
from dataclasses import dataclass
def something_else(klass):
return klass
def my_dataclass(klass):
return something_else(dataclass(klass))
my_dataclass
and/or something_else
应该如何注解表明return类型是数据类?
请参阅以下示例,了解内置 @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)
问题是 mypy 理解 metaclass decorator
及其魔力
关于__init__
,但是不明白 dataclass function
:
@dataclass
class Test:
a: int
# Mypy: Revealed type is "def (self: Test, a: builtins.int)"
reveal_type(Test.__init__)
class Test2:
a: int
#Mypy: Revealed type is "def (self: builtins.object)"
reveal_type(dataclass(Test2).__init__)
如您所见,Test2
的 __init__
方法不接受任何参数。
在 PEP 681 之前没有可行的方法来做到这一点。
A dataclass
描述的不是类型而是转换。 Python 的类型系统无法表达其实际效果 – @dataclass
由 MyPy Plugin which inspects the code, not just the types. This is triggered on specific decorators 在不了解其实现的情况下处理。
dataclass_makers: Final = {
'dataclass',
'dataclasses.dataclass',
}
虽然可以提供自定义 MyPy 插件,但这通常超出了大多数项目的范围。 PEP 681 (Python 3.11) 添加了一个通用的“这个装饰器的行为类似于 @dataclass
”-标记,可用于从注释到字段的所有转换器。
PEP 681 可通过 typing_extensions
.
执行数据classes
对于纯类型替代方案,定义您的自定义装饰器以获取数据class并修改它。一个数据class可以通过它的__dataclass_fields__
字段来识别。
from typing import Protocol, Any, TypeVar, Type
import dataclasses
class DataClass(Protocol):
__dataclass_fields__: dict[str, Any]
DC = TypeVar("DC", bound=DataClass)
def my_dataclass(klass: Type[DC]) -> Type[DC]:
...
这允许类型检查器理解并验证需要dataclass
class。
@my_dataclass
@dataclass
class TestB:
a: int
b: str
TestB(0, "") # note: Revealed type is "so_test.TestB"
@my_dataclass
class TestC: # error: Value of type variable "DC" of "my_dataclass" cannot be "TestC"
a: int
b: str
自定义 dataclass-like 装饰器
PEP 681 dataclass_transform
装饰器是 其他 装饰器的标记,表明它们的行为“像”@dataclass
。为了匹配 @dataclass
的行为,必须使用 field_specifiers
来指示字段以相同的方式表示。
from typing import dataclass_transform, TypeVar, Type
import dataclasses
T = TypeVar("T")
@dataclass_transform(
field_specifiers=(dataclasses.Field, dataclasses.field),
)
def my_dataclass(klass: Type[T]) -> Type[T]:
return something_else(dataclasses.dataclass(klass))
自定义数据class装饰器可以将所有关键字都作为@dataclass
。 dataclass_transform
可用于标记它们各自的默认值,即使装饰器本身不接受为关键字。