Mypy 可选字典错误,预期类型不是可选的

Mypy Optional dict error with an expected type that's not optional

我有一个 __init__ 函数,可以从三个不同的路径构建正确的对象。由于某些参数可以重复使用,因此它们在顶层 init 中具有默认值。

我不确定如何告诉 mypy 给定的参数在顶层 init 函数中是可选的并且对于给定的正确路径是必需的。

common_record.py:138: error: Argument 1 to "_init_from_common_record" of "CommonRecord" has incompatible type "Optional[Dict[Any, Any]]"; expected "Dict[Any, Any]"
common_record.py:142: error: Argument 1 to "_init_from_raw_data" of "CommonRecord" has incompatible type "Optional[Dict[Any, Any]]"; expected "Dict[Any, Any]"
Makefile:74: recipe for target 'type-check' failed
make: *** [type-check] Error 1
class CommonRecord:
    """A Common Record type. This is a json serializable object that contains
    sections for required and known fields that are common among data sources.
    """
    def __init__(
            self,
            record: Dict = None,
            raw_record: Dict = None,
            *,
            system: System = None,
            domain: Domain = None) -> None:
        """Initialization for the Common Record Class

        Three valid creation cases:
            * A single dictionary indicating a dictionary that's of the Common
            Record type
            * A normalized record and a raw record that will be turned into a
            Common Record.
            * A System object, a Domain object, and a raw record dictionary.
        """
        if not raw_record:
            self._init_from_common_record(record)
        elif (system and domain and raw_record):
            self._init_from_objects(system, domain, raw_record)
        else:
            self._init_from_raw_data(record, raw_record)

初始化函数的签名是

def _init_from_raw_data(self, record: Dict, raw_record: Dict) -> None:
  def _init_from_objects(
            self,
            system: System,
            domain: Domain,
            raw_record: Dict) -> None:
def _init_from_common_record(self, common_record: Dict) -> None:

您可以采用三种不同的方法。

首先,您可以修改条件以明确检查 record 是否为 None 并执行类似以下操作。

if not raw_record and not record:
    self._init_from_common_record(record)
elif (system and domain and raw_record):
    self._init_from_objects(system, domain, raw_record)
elif not record:
    self._init_from_raw_data(record, raw_record)
else:
    # Raise an exception here or something

其次,您可以添加断言来检查以确保 record 不是 None。

if not raw_record:
    assert record is not None
    self._init_from_common_record(record)
elif (system and domain and raw_record):
    self._init_from_objects(system, domain, raw_record)
else:
    assert record is not None
    self._init_from_raw_data(record, raw_record)

第三个解决方案是将 record 转换为正确的类型并完全跳过检查。不过,我不推荐这种方法——验证您的对象是否被正确使用将是更可靠的做法。


您还可以进行的一项额外但有些无关的改进是使用 overloads 改进构造函数的签名——基本上为每个构造 CommonRecord 的方法创建一个重载。这将有助于验证您是否始终正确地构建对象以及 "teach" mypy 如何验证我们在类型检查时在上面进行的一些运行时检查。

但是如果您希望您的实际实现正确进行类型检查,您仍然需要执行上面建议的三种方法之一。


您可以做的另一件事是通过将您的两个私有初始化方法转换为静态方法来完全回避这个问题,静态方法将构造 return 您的 CommonRecord 的新实例。

这将使您有可能简化构造函数并帮助您使类型更精确。当然,主要的缺点是现在实例化一个新的 CommonRecord 变得有点笨拙。