Trying to avoid typing issues I often run into the same problem.
E.g. I have a function x that very rarily returns value None, all other times it returns int.
def x(i: int) -> Union[int, None]:
if i == 0:
return
return i
def test(i: int):
a = x(i)
# typing issue: *= not supported for types int | None and int
a *= 25
x used very often in the codebase and most of the time i was already checked a hundred times that x(i) will indeed return int and not None.
Using it as int right away creates typing warnings - e.g. you can't multiply possible None value.
What's best practice for that case?
Ideas I considered:
- There is no real sense to check it for
Nonewithif a is None: returnas it's already known. a *= 25 # type: ignorewill makeaanUnknowntype.a = x(i) # type: intwill make the warning go away. But will create a new warning "int | None cannot be assigned to int"a = cast(int, x(i)), haven't tested it much yet.
I usually end up changing return type of x to just int, adding ignore in return # type: ignore and mention in the docstring that it can return None, it helps avoiding contaminating the entire codebase with type warnings. Is this the best approach?
def x(i: int) -> int:
"""might also return `None`"""
if i == 0:
return # type: ignore
return i
This might be a case where an exception is better than a return statement you never expect to be reached.
Code that is confident it has sufficiently validated the argument to
xcan omit thetrystatement.Statically speaking, this is accurate: if
xreturns, it is guaranteed to return anint. (Whether it will return is another question.)Ideally, you could define a refinement type like
NonZeroInt, and turni == 0into a type error, rather than a value error.