我正在尝试为接受序列的函数提供类型提示包含两个type
元素之一,而我不知如何使mypy
变得快乐。请注意,序列是同质的,这意味着类型不能混合,要么是非,要么是。通常,当它们是“兼容”类型时,例如路径str
或pathlib.Path
对象,并使用Union
进行注释就可以了。但是在序列的情况下,Sequence[Union[..]]
(或Union[Sequence[..]]
)似乎不起作用。这是一个最小的工作示例:
from pathlib import Path
from typing import Sequence, Dict, Union
def fn_accepts_dict(adict):
"""Function from an external module that accepts `dict`s."""
for key, val in adict.items():
print(f"{key}, {val}")
def vararg_test(resources: Sequence[Union[str, Dict]]):
"""My function where I want to provide type hints"""
if isinstance(resources[0], str):
resources2 = [{"path": Path(f)} for f in resources]
else:
resources2 = resources
for d in resources2:
fn_accepts_dict(d)
现在,使用上述定义,使用以下任一方法调用vararg_test
这两个工作按预期进行:
l1 = ["foo/bar", "bar/baz"]
l2 = [{"path": Path("foo/bar")}, {"path": Path("bar/baz")}]
但是运行mypy
给我以下错误:
type_hints.py:14: error: Argument 1 to "Path" has incompatible type "Union[str, Dict[Any, Any]]"; expected "Union[str, _PathLike[str]]"
type_hints.py:16: error: Incompatible types in assignment (expression has type "Sequence[Union[str, Dict[Any, Any]]]", variable has type "List[Dict[str, Path]]")
Found 2 errors in 1 file (checked 1 source file)
我该如何解决?
编辑:为了提供一些背景信息,str
是一个路径,dict
具有与该路径相对应的元数据,并且函数fn_accepts_dict
将元数据整理为一个元数据对象。因此,它们的逻辑流程为:str -> dict -> fn_accepts_dict
或dict -> fn_accepts_dict
。
虽然@ShadowRanger的建议看起来很有希望,但没有运气。我收到以下提示同样的错误:
def vararg_test2(resources: Union[Sequence[str], Sequence[Dict]]):
... # same implementation as above
mypy
错误:
type_hints.py:24: error: Argument 1 to "Path" has incompatible type "Union[str, Dict[Any, Any]]"; expected "Union[str, _PathLike[str]]"
type_hints.py:26: error: Incompatible types in assignment (expression has type "Union[Sequence[str], Sequence[Dict[Any, Any]]]", variable has type "List[Dict[str, Path]]")
我建议not根据参数的类型,尝试在一个函数中做两种不同的事情。而是定义两个不同的函数,每个函数采用一种特定的序列类型。
def do_with_strings(resources: Sequence[str]):
do_with_dicts([{"path": Path(f)} for f in resources])
def do_with_dicts(resources: Sequence[dict]):
for d in resources:
fn_accepts_dict(d)
对于您编写的代码,resources
的类型必须为ShadowRanger在注释中建议的Union[Sequence[str],Sequence[dict]]
,因为您假设整个列表的类型与第一个元素相同。
如果要保留异构类型,则需要检查每个元素以确定需要将其转换为dict
:
def vararg_test(resources: Sequence[Union[str, Dict]]):
for f in resources:
if isinstance(f, str):
f = {"path": Path(f)}
fn_accepts_dict(f)