In Python, is there a way to distinguish between strings and other iterables of strings?
A str
is valid as an Iterable[str]
type, but that may not be the correct input for a function. For example, in this trivial example that is intended to operate on sequences of filenames:
from typing import Iterable def operate_on_files(file_paths: Iterable[str]) -> None: for path in file_paths: ...
Passing in a single filename would produce the wrong result but would not be caught by type checking. I know that I can check for string or byte types at runtime, but I want to know if it’s possible to catch silly mistakes like that with a type-checking tool.
I’ve looked over the collections.abc
module and there doesn’t seem to be any abc that would include typical iterables (e.g. lists, tuples) but exclude strings. Similarly, for the typing
module, there doesn’t seem to be a type for iterables that don’t include strings.
Advertisement
Answer
As of March 2022, the answer is no.
This issue has been discussed since at least July 2016. On a proposal to distinguish between str
and Iterable[str]
, Guido van Rossum writes:
Since
str
is a valid iterable ofstr
this is tricky. Various proposals have been made but they don’t fit easily in the type system.
You’ll need to list out all of the types that you want your functions to accept explicitly, using Union
(pre-3.10) or |
(3.10 and higher).
e.g. For pre-3.10, use:
from typing import Union ## Heading ## def operate_on_files(file_paths: Union[TypeOneName, TypeTwoName, etc.]) -> None: for path in file_paths: ...
For 3.10 and higher, use:
## Heading ## def operate_on_files(file_paths: TypeOneName | TypeTwoName | etc.) -> None: for path in file_paths: ...
If you happen to be using Pytype, it will not treat str
as an Iterable[str]
(as pointed out by Kelly Bundy). But, this behavior is typechecker-specific, and isn’t widely supported in other typecheckers.