I have a function that uses the len
function on one of it's parameters and iterates over the parameter. Now I can choose whether to annotate the type with Iterable
or with Sized
, but both gives errors in mypy
.
from typing import Sized, Iterable def foo(some_thing: Iterable): print(len(some_thing)) for part in some_thing: print(part)
Gives
error: Argument 1 to "len" has incompatible type "Iterable[Any]"; expected "Sized"
While
def foo(some_thing: Sized): ...
Gives
error: Iterable expected error: "Sized" has no attribute "__iter__"
Since there is no Intersection
as discussed in this issue I need to have some kind of mixed class.
from abc import ABCMeta from typing import Sized, Iterable class SizedIterable(Sized, Iterable[str], metaclass=ABCMeta): pass def foo(some_thing: SizedIterable): print(len(some_thing)) for part in some_thing: print(part) foo(['a', 'b', 'c'])
This gives an error when using foo
with a list
.
error: Argument 1 to "foo" has incompatible type "List[str]"; expected "SizedIterable"
This is not too surprising since:
>>> SizedIterable.__subclasscheck__(list) False
So I defined a __subclasshook__
(see docs).
class SizedIterable(Sized, Iterable[str], metaclass=ABCMeta): @classmethod def __subclasshook__(cls, subclass): return Sized.__subclasscheck__(subclass) and Iterable.__subclasscheck__(subclass)
Then the subclass check works:
>>> SizedIterable.__subclasscheck__(list) True
But mypy
still complains about my list
.
error: Argument 1 to "foo" has incompatible type "List[str]"; expected "SizedIterable"
How can I use type hints when using both the len
function and iterate over my parameter? I think casting foo(cast(SizedIterable, ['a', 'b', 'c']))
is not a good solution.
Type hints work best in modern Pythons. Annotations were introduced in Python 3.0, and it's possible to use type comments in Python 2.7. Still, improvements like variable annotations and postponed evaluation of type hints mean that you'll have a better experience doing type checks using Python 3.6 or even Python 3.7.
In short, a TypeVar is a variable you can use in type signatures so you can refer to the same unspecified type more than once, while a NewType is used to tell the type checker that some values should be treated as their own type.
Unlike how types work in most other statically typed languages, type hints by themselves don't cause Python to enforce types. As the name says, type hints just suggest types. There are other tools, which you'll see later, that perform static type checking using type hints.
Type annotations — also known as type signatures — are used to indicate the datatypes of variables and input/outputs of functions and methods. In many languages, datatypes are explicitly stated. In these languages, if you don't declare your datatype — the code will not run.
Starting from Python3.6 there's a new type called Collection
. See here.
In the future Protocol
s will be introduced. They are already available through typing_extensions
. See also PEP 544. Using Protocol
the code above would be:
from typing_extensions import Protocol class SizedIterable(Protocol): def __len__(self): pass def __iter__(self): pass def foo(some_thing: SizedIterable): print(len(some_thing)) for part in some_thing: print(part) foo(['a', 'b', 'c'])
mypy
takes that code without complaining. But PyCharm is saying
Expected type 'SizedIterable', got 'List[str]'
about the last line.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With