Reputation: 4174
Take the following function:
from typing import Optional
def area_of_square(width: Optional[float] = None,
height: Optional[float] = None) -> float:
if width is None and height is None:
raise ValueError('You have not specified a width or height')
if width is not None and height is not None:
raise ValueError('Please specify a width or height, not both')
area = width**2 if width is not None else height**2
return area
At the area =
line, mypy complains that height
could be None.
I could add the following line just above it:
height = typing.cast(int, height)
But that is not correct, since height
could be None
. Wrapping that cast in any sort of logic makes mypy get lost and I'm back to the error.
I personally use typing for readability and to avoid bugs. Getting errors like this (and often with lazy initialization and other similar uses of None
) kind of defeats the purpose, so I like to fix them when it makes sense.
What are some strategies people use in this scenario?
Upvotes: 1
Views: 3991
Reputation: 7923
mypy
cannot bind multiple variables with one common condition.
The following lines type guard both variables:
a is None and b is None
a is not None and b is not None
So they work as expected, while another condition:
a is not None or b is not None
is not informative for mypy
, you cannot express "at least one of them is not None
" and use it in type checking.
I'd do this instead:
from typing import Optional
def area_of_square(width: Optional[float] = None,
height: Optional[float] = None) -> float:
if width is not None and height is not None:
raise ValueError('Please specify a width or height, not both')
elif width is not None:
area = width**2
elif height is not None:
area = height**2
else:
raise ValueError('You have not specified a width or height')
return area
Upvotes: 4