I am looking for something similar to this and I will give an example:
words : list[str] = get_words()
if not isinstance(words, list[str]):
raise TypeError(f'Returned object is not a list of strings: {words}')
but this is not possible, instead I have to do:
words: list[str] = get_words()
if not (isinstance(words, list) and all(isinstance(w, str) for w in words)):
raise TypeError(f'Returned object is not a list of strings: {words!r}')
which is less readable. Is this a limitation of python? or maybe there is something out there that I am not aware of that I should be using? This is specially problematic if we have longer types.
This is a feature of Python, because type checks are supposed to be fast.
If you have a list of 10_000_000 strings, all(isinstance(w, str) for w in words) can take 1s to compute. Which I think would be an unacceptable footgun.
If you do want to assert that all elements of a list are strings, explicit iteration (as you do) is a proper way to do it.
PS: I would find it more readable as
assert isinstance(words, list)
assert all(isinstance(w, str) for w in words)
Those checks are best done before run time with a type checker like mypy.
At run time, I don’t know of a better native solution than what you’ve already come up with. But you could use a third party library, e.g. typeguard
>>> typeguard.check_type([1,2,'a'],list[str])
...
typeguard.TypeCheckError: item 0 of list is not an instance of str
>>> typeguard.check_type(['b','a','f'],list[str])
['b', 'a', 'f']
Thanks for your reply. I cannot do that check before run time. The data comes from a file, whose content I have no control over. This should be a cleanup step.
Yes, the typeguard thing is what I was thinking of, basically a smarter version of isinstance that would help me to keep the code cleaner. As I mentioned before, this would be specially useful with types like:
dict[str,set[str]]
which are too complicated to be done with isinstance while keeping the code clean.
Thanks for your reply. The thing is that I need checks like these in order to make sure the input data is correct. This is a validation step and if the data does not satisfy it, whatever happens later will raise an exception anyway. I would expect a check for only the type to be faster.
The check you suggest is cleaner. However it is still not as good as just doing:
isinstance(object, dict[str, set[int])
I see from the comment below that typeguard can do this type of thing and I will try to see if I can integrate it in my code, given that no other alternative in the standard library exists.
Probably the best builtin way to do this is using TypeIs.
from typing import Any, TypeIs
def is_list_of_str(lst: Any) -> TypeIs[list[str]]:
return isinstance(lst, list) and all(isinstance(elem, str) for elem in lst)
When a function returns a TypeIs[T] it means that if the returned value is True, it is the type T, otherwise it isn’t. In Python versions before 3.13, use TypeGuard instead, which is a bit less capable.
The OP specifically stated that they do not want type checking time validation, but instead runtime validation.
Generally speaking, to answer the OPs question, yes, there are other ways to check this, and creating your own isinstance shouldn’t be too hard either (coming from a guy who did that already).