Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is mypy complaining about list comprehension when it can't be annotated?

Why does Mypy complain that it requires a type annotation for a list comprehension variable, when it is impossible to annotate such a variable using MyPy?

Specifically, how can I resolve the following error:

from enum import EnumMeta

def spam( y: EnumMeta ):
    return [[x.value] for x in y] 🠜 Mypy: Need type annotation for 'x' 

cast doesn't work:

return [[cast(Enum, x).value] for x in y] 🠜 Mypy: Need type annotation for 'x'  

Even though Mypy doesn't support annotations (x:Enum) in such a case I see the usage of the variable can be annotated using cast (see this post). However, cast(Enum, x) doesn't stop Mypy complaining that the variable isn't annotated in the first place.

#type: doesn't work:

return [[x.value] for x in y] # type: Enum 🠜 Mypy: Misplaced type annotation

I also see that a for loop variable can be annotated using a comment, # type: (see this post). However, # type: Enum doesn't work for list comprehension's for.

like image 452
c z Avatar asked Mar 13 '20 11:03

c z


People also ask

Are list comprehensions good?

List comprehensions are useful and can help you write elegant code that's easy to read and debug, but they're not the right choice for all circumstances. They might make your code run more slowly or use more memory.

Is MYPY slow?

Mypy runs are slow If your mypy runs feel slow, you should probably use the mypy daemon, which can speed up incremental mypy runtimes by a factor of 10 or more. Remote caching can make cold mypy runs several times faster.

What is MYPY used for?

“Mypy is an optional static type checker for Python that aims to combine the benefits of dynamic (or 'duck') typing and static typing. Mypy combines the expressive power and convenience of Python with a powerful type system and compile-time type checking.” A little background on the Mypy project.

Are list comprehensions bad?

It's not considered bad. are generally frowned upon. In other words, readability is important. Basically, if you're not storing the result, and/or it modifies some other object, then a list comprehension is probably a bad idea.


2 Answers

In a list comprehension, the iterator must be cast instead of the elements.

from typing import Iterable, cast
from enum import EnumMeta, Enum

def spam(y: EnumMeta):
    return [[x.value] for x in cast(Iterable[Enum], y)]

This allows mypy to infer the type of x as well. In addition, at runtime it performs only 1 cast instead of n casts.

If spam can digest any iterable that produces enums, it is easier to type hint this directly.

from typing import Iterable
from enum import Enum

def spam(y: Iterable[Enum]):
    return [[x.value] for x in y]
like image 80
MisterMiyagi Avatar answered Sep 27 '22 22:09

MisterMiyagi


MisterMyagi's answer is the best solution for this specific case. It may be worth noting, however, that it is also possible to declare the type of a variable before that variable is defined. The following also passes MyPy:

from enum import Enum

def spam(y: type[Enum]):
    x: Enum
    return [[x.value] for x in y]
like image 21
Alex Waygood Avatar answered Sep 28 '22 00:09

Alex Waygood