Python 接受多种类型之一的(同构)序列的函数的类型提示
我试图为接受序列的函数提供类型提示 有两种Python 接受多种类型之一的(同构)序列的函数的类型提示,python,python-3.x,type-hinting,mypy,Python,Python 3.x,Type Hinting,Mypy,我试图为接受序列的函数提供类型提示 有两种类型的元素中的一种,我不知道如何使mypy快乐。请注意,序列是同质的,这意味着类型不能混合,或者。通常,当它们是“兼容”类型时,我会这样做,例如路径str或pathlib.path对象,并用Union注释它们就可以了。但在序列的情况下,sequence[Union[…]](或Union[sequence[…]]])似乎不起作用。下面是一个简单的工作示例: from pathlib import Path from typing import Sequen
类型的元素中的一种,我不知道如何使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\u 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\u接受\u dict
将元数据整理成一个元数据对象。所以他们的逻辑流程是:str->dict->fn\u接受\u dict
或者,dict->fn\u接受\u 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]]")
编辑2:不幸的是,在所有注释中,它看起来更像C/C++而不是Python;请参阅下面的my,以获得更具python风格的解决方案。我建议不要尝试在一个函数中根据参数的类型执行两种不同的操作。相反,定义两个不同的函数,每个函数采用特定类型的序列
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)
对于编写的代码,资源
的类型必须是,正如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)
根据参数的类型,我建议不要尝试在一个函数中做两件不同的事情。相反,定义两个不同的函数,每个函数采用特定类型的序列
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)
对于编写的代码,资源
的类型必须是,正如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)
TLDR:mypy
isinstance(v,tp)
关于v
。它不理解关于v
的isinstance(v,tp)
,例如v[0]:str
表示v:List[str]
的类型
mypy
不理解isinstance(参考资料[0],str)
在资源:序列[str]
和资源:序列[Dict]
之间进行分支。这意味着resources=resources2
推断两者是完全相同的类型,在分配或使用resources2
时中断
必须对resources2
进行注释以防止推断的类型相等,并在分支中对cast
进行注释以将其标记为相等
def vararg_test(resources: Union[Sequence[str], Sequence[Dict]]):
"""My function where I want to provide type hints"""
resources2: Sequence[Dict] # fixed type to prevent inferred equality
if isinstance(resources[0], str): # Mypy does not recognise this branch by itself!
# exclude second Union branch
resources = cast(Sequence[str], resources)
resources2 = [{"path": Path(f)} for f in resources]
else:
# exclude first Union branch
resources2 = cast(Sequence[Dict], resources)
for d in resources2:
fn_accepts_dict(d)
TLDR:mypy
isinstance(v,tp)
关于v
。它不理解关于v
的isinstance(v,tp)
,例如v[0]:str
表示v:List[str]
的类型
mypy
不理解isinstance(参考资料[0],str)
在资源:序列[str]
和资源:序列[Dict]
之间进行分支。这意味着resources=resources2
推断两者是完全相同的类型,在分配或使用resources2
时中断
必须对resources2
进行注释以防止推断的类型相等,并在分支中对cast
进行注释以将其标记为相等
def vararg_test(resources: Union[Sequence[str], Sequence[Dict]]):
"""My function where I want to provide type hints"""
resources2: Sequence[Dict] # fixed type to prevent inferred equality
if isinstance(resources[0], str): # Mypy does not recognise this branch by itself!
# exclude second Union branch
resources = cast(Sequence[str], resources)
resources2 = [{"path": Path(f)} for f in resources]
else:
# exclude first Union branch
resources2 = cast(Sequence[Dict], resources)
for d in resources2:
fn_accepts_dict(d)
正如@MisterMiyagi的回答所指出的,问题源于mypy
在isinstance
中使用表达式时无法推断类型。因此,我尝试了另一种实现,其中isinstance
被传递一个名称。这也有一个好处,vararg_test
现在可以接受任何iterable
def vararg_test(resources: Iterable[Union[str, Dict]]):
"""My function where I want to use type hints"""
resources2: List[Dict] = []
for res in resources:
if isinstance(res, str):
res = {"path": Path(res)}
resources2 += [res]
for d in resources2:
fn_accepts_dict(d)
然而,这仍然需要我们注释resources2
,但在不需要任何注释的情况下,很容易将其重新表述为列表理解
def vararg_test3(resources: Iterable[Union[str, Dict]]):
"""My function where I want to use type hints"""
resources2 = [
{"path": Path(res)} if isinstance(res, str) else res
for res in resources
]
for d in resources2:
fn_accepts_dict(d)
正如@MisterMiyagi的回答所指出的,问题源于mypy
在isinstance
中使用表达式时无法推断类型。因此,我尝试了另一种实现,其中isinstance
被传递一个名称。这也有一个好处,vararg_test
现在可以接受任何iterable
def vararg_test(resources: Iterable[Union[str, Dict]]):
"""My function where I want to use type hints"""
resources2: List[Dict] = []
for res in resources:
if isinstance(res, str):
res = {"path": Path(res)}
resources2 += [res]
for d in resources2:
fn_accepts_dict(d)
然而,这仍然需要我们注释resources2
,但在不需要任何注释的情况下,很容易将其重新表述为列表理解
def vararg_test3(resources: Iterable[Union[str, Dict]]):
"""My function where I want to use type hints"""
resources2 = [
{"path": Path(res)} if isinstance(res, str) else res
for res in resources
]
for d in resources2:
fn_accepts_dict(d)
是否尝试将接头
移出一个标高<代码>联合[序列[str],序列[Dict]
?看起来您实际上传递了一个类型相同的序列。但是,如果您想接受异构序列,则不一定要进行修复。这样的异构序列将无法使用所需的用法,因为Path
不能将dict
作为参数。特别是因为资源[0]
是str
并不意味着资源
的每个元素都是可以传递到路径的str
。如前所述,暗影游侠提出的通过同质序列的联合的建议是正确的