非特定数据类实例的类型提示

时间:2019-02-13 10:32:01

标签: python protocols python-3.7 python-dataclasses

我有一个函数可以接受任何dataclass的实例。 什么是合适的类型提示呢?

在python文档中找不到任何官方内容


这是我一直在做的,但我认为这是不正确的

from typing import Any, NewType

DataClass = NewType('DataClass', Any)
def foo(obj: DataClass):
    ...

另一个想法是将Protocol与这些类属性__dataclass_fields____dataclass_params__一起使用。

1 个答案:

答案 0 :(得分:2)

尽管dataclasses.dataclass的名称不公开类接口。它仅允许您以一种方便的方式声明一个自定义类,从而使它很明显将被用作数据容器。因此,从理论上讲,几乎没有机会编写仅适用于数据类的内容,因为数据类实际上只是普通类。

在实践中,有两个原因导致您仍然要声明仅数据类的函数,而我看到了两种解决方法。


正确方式,使用静态类型检查器并编写协议

from dataclasses import dataclass
from typing import Dict

from typing_extensions import 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 A:
    pass

dataclass_only(A())  # a static type check should show that this line is fine

此方法也是您在问题中提到的方法,但它具有三个缺点:

  • 您需要第三方库(例如mypy)来为您执行静态类型检查
  • 您还需要安装typing_extensions,因为Protocol尚未成为核心typing模块的一部分
  • 最后但并非最不重要的是,以这种方式对数据类使用协议doesn't work right now

EAFP启发的东西实际上更有效

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!")
    ...

在这种方法中,由于文档的原因,对于此代码的维护者或用户而言,行为仍然非常清楚。但是不利之处在于,您不会立即对代码进行静态分析(包括IDE的类型提示)。