尽管它的名字是dataclasses.dataclass
,但它并没有公开类接口。它只允许您以方便的方式声明自定义类,使其明显地将被用作数据容器。因此,理论上很难编写仅适用于dataclasses的代码,因为dataclasses实际上只是普通的类。
在实践中,你仍有几个原因希望声明仅适用于dataclasses的函数,下面是如何实现:
from dataclasses import dataclass
from typing import ClassVar, Dict, Protocol
class IsDataclass(Protocol):
__dataclass_fields__: ClassVar[Dict]
def dataclass_only(x: IsDataclass):
...
@dataclass
class Foo:
pass
class Bar:
pass
dataclass_only(Foo())
dataclass_only(Bar())
这种方法也是你在问题中暗示的。如果你想采用这种方法,请注意你需要使用第三方库,例如mypy
来为你进行静态类型检查。如果你使用的是Python 3.7
或更早的版本,则需要手动安装typing_extensions
,因为Protocol
仅成为了Python 3.8
标准库的一部分。
还要注意,较旧版本的mypy(>=0.982
)错误地期望__dataclass_fields__
是一个实例属性,所以协议应该只是__dataclass_fields__: Dict
[1]。
当我最初编写此帖时,它还包括了The Old Way of Doing Things,即我们没有类型检查器时的方法。我把它保留下来,但不推荐再使用运行时失败处理这种特性:
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!")
...
[1]感谢 @Kound 更新并测试了ClassVar
的行为。
@dataclass
装饰器和不使用的类之间没有明显差异。Dataclasses不实现任何特殊方法,也没有任何特殊属性。区分“dataclass”和“regular”类毫无意义。 - Aran-Feydataclasses.astuple
这样的函数只适用于数据类。 - Aran-Feyastuple
方法的Protocol
?听起来不错,但有点不稳定。不确定为什么他们决定使用装饰器而不是通过继承和元类像namedtuple
一样创建dataclass
。 - mosheviastuple
不是一个方法,所以那样做行不通。我认为这不能通过typing
来完成,因为数据类在技术上不是一种类型。它们不公开基类或特定的公共接口。换句话说,接收到非数据类而不是数据类更接近于 ValueError 而不是 TypeError。 - Aran-Fey_is_dataclass_instance
的函数。它检查是否具有属性__dataclass_fields__
,我认为这已经是最好的了。 - moshevi