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