mypy icon indicating copy to clipboard operation
mypy copied to clipboard

“Cannot infer type of lambda” using default parameter to recapture loop variable

Open andersk opened this issue 2 years ago • 2 comments

Bug Report

One of the most common gotchas in Python is that a loop reassigns its iteration variable rather than creating a new binding for each iteration, and a lambda closure created in the loop observes this reassigned value rather than the value from each iteration. A typical workaround is to add a “redundant” default parameter to the lambda to recapture the value from each iteration—for example, the i=i from the above link (reproduced below). But mypy rejects such a lambda with error: Cannot infer type of lambda [misc].

To Reproduce

from typing import Callable

def create_multipliers() -> list[Callable[[int], int]]:
    return [lambda x, i=i: i * x for i in range(5)]

(mypy playground)

Expected Behavior

mypy ought to be able to realize that since the lambda is coerced to Callable[[int], int], its second parameter i=i can only ever receive the default value, so its type should be successfully inferred as (x: int, i: int = ...) -> int.

Actual Behavior

main.py:4: error: Cannot infer type of lambda  [misc]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.3.0
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.11.3

andersk avatar Jun 18 '23 19:06 andersk

To address this issue, you can provide an explicit type annotation for the lambda expression. This helps MyPy understand the intended types and avoid the type inference problem.

myselfAbdullah007 avatar Jun 26 '23 18:06 myselfAbdullah007

@myselfAbdullah007 No, you can’t: the Python syntax does not allow lambda parameters to be annotated. Anyway, I’m not looking for support; I’m plenty capable of working around this in my own code. I filed this issue so it can be fixed in mypy for everyone.

andersk avatar Jun 26 '23 22:06 andersk