Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 接受多种类型之一的(同构)序列的函数的类型提示_Python_Python 3.x_Type Hinting_Mypy - Fatal编程技术网

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
。如前所述,暗影游侠提出的通过同质序列的联合的建议是正确的