Consider the following code:
from typing import Callable, TypeVar
T = TypeVar('T')
def middle_man(
    producer: Callable[[], T],
    consumer: Callable[[T], None]
) -> None:
    consumer(producer())
middle_man(
    lambda: "HELLO",
    lambda s: print(s.lower())
)
This code runs without error and works as expected, however, mypy fails to infer the type of s in the second lambda, giving the error:
"object" has no attribute "lower"`
Right now, my only workaround is either to cast s, which can be a pain in a more complex lambda or with a more complex type, or to add # type: ignore, which I'd rather not do.
Is there a better workaround, or a proper way to get mypy to recognize the type?
Just like a normal function, a Lambda function can have multiple arguments with one expression. In Python, lambda expressions (or lambda forms) are utilized to construct anonymous functions. To do so, you will use the lambda keyword (just as you use def to define normal functions).
A lambda function can take any number of arguments, but can only have one expression.
Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. auto y = [] (int first, int second) { return first + second; };
It seems to me that you have two conflicting concerns here:
T TypeVar to be generic so that the type annotation for the producer and consumer parameters to middle_man can be flexiblemiddle_man to be specific enough that mypy recognizes that you are calling it with a str typeIn order to achieve this, I would annotate the arguments to a specific call to middle_man by storing the arguments as variables first:
from typing import Callable, TypeVar
T = TypeVar('T')
def middle_man(
    producer: Callable[[], T],
    consumer: Callable[[T], None]
) -> None:
    consumer(producer())
producer: Callable[[], str] = lambda: "HELLO"
consumer: Callable[[str], None] = lambda s: print(s.lower())
middle_man(producer, consumer)
EDIT
Another suggestion:
Since you are using a generic TypeVar anyway, you can replace it with the Any type without losing type information and mypy will not raise an error because it skips static type checking for Any types.
from typing import Callable, Any
def middle_man(
    producer: Callable[[], Any],
    consumer: Callable[[Any], None]
) -> None:
    consumer(producer())
middle_man(
    lambda: "HELLO",
    lambda s: print(s.lower())
)
I know this solution is not very satisfying because it essentially defeats the purpose of using mypy for static type checking but unless you want to restrict the upper bound of your TypeVar to a class with a lower method then this might be your best option.
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