Mypy Optional dict错误,预期类型不是可选的

时间:2019-05-14 21:57:46

标签: python-3.x mypy

我有一个__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)

具有init函数的签名

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:

1 个答案:

答案 0 :(得分:1)

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

首先,您可以修改条件以显式检查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不为空。

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的方法都会产生一个重载。这将有助于验证您始终在正确构造对象,并“教” mypy如何在类型检查时验证我们上面所做的一些运行时检查。

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


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

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