在Python中,有没有办法区分字符串和字符串的其他可迭代对象?
A
str
作为 Iterable[str]
类型是有效的,但这可能不是函数的正确输入。例如,在这个简单的示例中,旨在对文件名序列进行操作:
from typing import Iterable
def operate_on_files(file_paths: Iterable[str]) -> None:
for path in file_paths:
...
传入单个文件名会产生错误的结果,但不会被类型检查捕获。我知道我可以在运行时检查字符串或字节类型,但我想知道是否可以使用类型检查工具捕获类似的愚蠢错误。
我查看了
collections.abc
模块,似乎没有任何 abc 包含典型的可迭代对象(例如列表、元组)但不包括字符串。同样,对于 typing
模块,似乎没有不包含字符串的可迭代类型。
这个问题至少从 2016 年 7 月就开始讨论。关于区分
str
和 Iterable[str]
的提议,Guido van Rossum 写道:
因为
是str
的有效迭代,这很棘手。已经提出了各种建议,但它们不容易适合类型系统。str
您需要使用 Union
(3.10 之前)或
|
(3.10 及更高版本)列出您希望函数显式接受的所有类型。例如对于 3.10 之前的版本,请使用:
from typing import Union
## Heading ##
def operate_on_files(file_paths: Union[TypeOneName, TypeTwoName, etc.]) -> None:
for path in file_paths:
...
对于 3.10 及更高版本,请使用:
## Heading ##
def operate_on_files(file_paths: TypeOneName | TypeTwoName | etc.) -> None:
for path in file_paths:
...
如果您碰巧使用 Pytype,它不会将 str
视为
Iterable[str]
(正如 Kelly Bundy 所指出的)。但是,这种行为是特定于类型检查器的,并且在其他类型检查器中并未得到广泛支持。
@overload
和
Never
作为返回类型。
from typing import overload, Never
from collections.abc import Iterable # Or Sequence
@overload
def first(v: str) -> Never:
...
@overload
def first(v: Iterable[str]) -> str:
...
def first(v: Iterable[str]) -> str:
return next(iter(v))
但这还不够。 Mypy 仍然会认为以下内容没问题:
first(['foo']) # Fine
first('foo') # Anything after this line is simply ignored.
a = 'bar'
reveal_type(a) # Silently emit nothing.
--warn-unreachable
标志允许您配置此行为。有了它,Mypy将引发错误,表示该行无法访问:
first(['foo']) # Fine
first('foo') # Still no error, however.
a = 'bar' # error: Statement is unreachable
另一方面,虽然没有引发任何错误,但在幕后使用 Pyright 的 Pylance 会淡出无法访问的代码:它只对显式类型提示的代码这样做: