非特定数据类实例的类型提示
type hint for an instance of a non specific dataclass
我有一个函数可以接受任何 dataclass
的实例。
什么是适合它的类型提示?
尚未在 python 文档中找到官方内容
这是我一直在做的,但我认为它不正确
from typing import Any, NewType
DataClass = NewType('DataClass', Any)
def foo(obj: DataClass):
...
另一个想法是使用具有这些 class 属性的 Protocol
__dataclass_fields__
、__dataclass_params__
.
尽管名称如此,dataclasses.dataclass
并未公开 class 接口。它只是允许您以一种方便的方式声明自定义 class ,这使得它很明显将被用作数据容器。因此,理论上,几乎没有机会编写仅适用于 dataclasses 的东西,因为 dataclasses 实际上只是普通的 classes。
在实践中,有几个原因导致您想要声明数据class-only 函数,您应该这样做:
from dataclasses import dataclass
from typing import Dict, Protocol
class IsDataclass(Protocol):
# as already noted in comments, checking for this attribute is currently
# the most reliable way to ascertain that something is a dataclass
__dataclass_fields__: Dict
def dataclass_only(x: IsDataclass):
... # do something that only makes sense with a dataclass
@dataclass
class Foo:
pass
class Bar:
pass
dataclass_only(Foo()) # a static type check should show that this line is fine ..
dataclass_only(Bar()) # .. and this one is not
这个方法也是你在问题中提到的。如果你想这样做,请记住你需要一个第三方库,例如 mypy
to do the static type checking for you, and if you are on python 3.7
or earlier, you need to manually install typing_extensions
因为 Protocol
在 3.8
.[=19 中才成为标准库的一部分=]
当我第一次写它时,这个 post 也以旧的做事方式为特色,那时候我们不得不在没有类型检查器的情况下凑合。我放弃了,但不建议再处理这种仅运行时失败的功能:
from dataclasses import is_dataclass
def dataclass_only(x):
"""Do something that only makes sense with a dataclass.
Raises:
ValueError if something that is not a dataclass is passed.
... more documentation ...
"""
if not is_dataclass(x):
raise ValueError(f"'{x.__class__.__name__}' is not a dataclass!")
...
可以使用一个名为 is_dataclass
的辅助函数,它是从 dataclasses
导出的。
基本上它的作用是这样的:
def is_dataclass(obj):
"""Returns True if obj is a dataclass or an instance of a
dataclass."""
cls = obj if isinstance(obj, type) else type(obj)
return hasattr(cls, _FIELDS)
它使用 type 获取实例的类型,或者如果对象扩展了 type,则获取对象本身。
然后检查此对象上是否存在变量 _FIELDS,它等于 __dataclass_fields__
。这基本上等同于这里的其他答案。
要“键入”数据类,我会这样做:
class DataclassProtocol(Protocol):
__dataclass_fields__: Dict
__dataclass_params__: Dict
__post_init__: Optional[Callable]
我有一个函数可以接受任何 dataclass
的实例。
什么是适合它的类型提示?
尚未在 python 文档中找到官方内容
这是我一直在做的,但我认为它不正确
from typing import Any, NewType
DataClass = NewType('DataClass', Any)
def foo(obj: DataClass):
...
另一个想法是使用具有这些 class 属性的 Protocol
__dataclass_fields__
、__dataclass_params__
.
尽管名称如此,dataclasses.dataclass
并未公开 class 接口。它只是允许您以一种方便的方式声明自定义 class ,这使得它很明显将被用作数据容器。因此,理论上,几乎没有机会编写仅适用于 dataclasses 的东西,因为 dataclasses 实际上只是普通的 classes。
在实践中,有几个原因导致您想要声明数据class-only 函数,您应该这样做:
from dataclasses import dataclass
from typing import Dict, Protocol
class IsDataclass(Protocol):
# as already noted in comments, checking for this attribute is currently
# the most reliable way to ascertain that something is a dataclass
__dataclass_fields__: Dict
def dataclass_only(x: IsDataclass):
... # do something that only makes sense with a dataclass
@dataclass
class Foo:
pass
class Bar:
pass
dataclass_only(Foo()) # a static type check should show that this line is fine ..
dataclass_only(Bar()) # .. and this one is not
这个方法也是你在问题中提到的。如果你想这样做,请记住你需要一个第三方库,例如 mypy
to do the static type checking for you, and if you are on python 3.7
or earlier, you need to manually install typing_extensions
因为 Protocol
在 3.8
.[=19 中才成为标准库的一部分=]
当我第一次写它时,这个 post 也以旧的做事方式为特色,那时候我们不得不在没有类型检查器的情况下凑合。我放弃了,但不建议再处理这种仅运行时失败的功能:
from dataclasses import is_dataclass
def dataclass_only(x):
"""Do something that only makes sense with a dataclass.
Raises:
ValueError if something that is not a dataclass is passed.
... more documentation ...
"""
if not is_dataclass(x):
raise ValueError(f"'{x.__class__.__name__}' is not a dataclass!")
...
可以使用一个名为 is_dataclass
的辅助函数,它是从 dataclasses
导出的。
基本上它的作用是这样的:
def is_dataclass(obj):
"""Returns True if obj is a dataclass or an instance of a
dataclass."""
cls = obj if isinstance(obj, type) else type(obj)
return hasattr(cls, _FIELDS)
它使用 type 获取实例的类型,或者如果对象扩展了 type,则获取对象本身。
然后检查此对象上是否存在变量 _FIELDS,它等于 __dataclass_fields__
。这基本上等同于这里的其他答案。
要“键入”数据类,我会这样做:
class DataclassProtocol(Protocol):
__dataclass_fields__: Dict
__dataclass_params__: Dict
__post_init__: Optional[Callable]