Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typehints for Sized Iterable in Python

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.

like image 463
Benjamin Avatar asked Mar 22 '18 11:03

Benjamin


People also ask

Should you use type hints in Python?

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.

What is TypeVar?

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.

Does Python enforce type hints?

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.

What is type annotation in Python?

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.


2 Answers

Starting from Python3.6 there's a new type called Collection. See here.

like image 139
Avision Avatar answered Sep 29 '22 03:09

Avision


In the future Protocols 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.

like image 30
Benjamin Avatar answered Sep 29 '22 02:09

Benjamin