mypy, type hint: Union[float, int] -> is there a Number type?
Solution 1
Use float
only, as int
is implied in that type:
def my_func(number: float):
PEP 484 Type Hints specifically states that:
Rather than requiring that users write import numbers and then use
numbers.Float
etc., this PEP proposes a straightforward shortcut that is almost as effective: when an argument is annotated as having typefloat
, an argument of typeint
is acceptable; similar, for an argument annotated as having type complex, arguments of type float or int are acceptable.
(Bold emphasis mine).
Ideally you would still use numbers.Real
:
from numbers import Real
def my_func(number: Real):
as that would accept fractions.Fraction()
and decimal.Decimal()
objects as well; the number pyramid is broader than just integers and floating point values.
However, these are not currently working when using mypy
to do your type checking, see Mypy #3186.
Solution 2
You can define your own type to address this and keep your code cleaner.
FloatInt = Union[float, int]
def my_func(number: FloatInt):
# Do something
Solution 3
Python > 3.10
allows you to do the following.
def my_func(number: int | float) -> int | float:
Solution 4
For people who come to this question for the more general problem of Union typing hints for entities which don't have an existing supertype in common, for example Union[int, numpy.ndarray]
, the solution is to import Union
from typing
.
Example 1:
from typing import Union
def my_func(number: Union[float, int]):
# Do something
Example 2:
from typing import Union
import numpy as np
def my_func(x: Union[float, np.ndarray]):
# do something
# Do something
Related videos on Youtube
![JPFrancoia](https://i.stack.imgur.com/yPT8c.jpg?s=256&g=1)
JPFrancoia
Updated on October 08, 2021Comments
-
JPFrancoia over 2 years
mypy is really handy and catches a lot of bugs, but when I write "scientific" applications, I often end up doing:
def my_func(number: Union[float, int]): # Do something
number
is either a float or int, depending on the user's input. Is there an official way to do that? -
user1475412 about 5 yearsTangentially related, but
Decimal
is not actually a subclass ofReal
, or part of the numeric tower at all: python.org/dev/peps/pep-3141/#the-decimal-type -
EliadL about 3 yearsGood idea, but
int
is officially redundant whenfloat
is present. See this answer: stackoverflow.com/a/50928627/4960855 -
Alex Waygood over 2 yearsAs Martijn's answer points out, the mypy documentation clearly states that "when an argument is annotated as having type float, an argument of type int is acceptable". Explicitly annotating the union here is pointless.
-
Alex Waygood over 2 years@user1475412 — interestingly,
decimal.Decimal
is now registered as a virtual subclass ofnumbers.Number
(though still isn't a virtual subclass ofnumbers.Real
). I think the decision to add it as a virtual subclass must have been made some time after the PEP was written. github.com/python/cpython/blob/… -
Martijn Pieters over 2 years@AlexWaygood: I think you misundrestood something;
decimal.Decimal
has been registered as aNumber
for 13 years now, well before the PEP. It still isn't a virtual subclass ofnumbers.Real
, which is what user1475412 is talking about. -
Alex Waygood over 2 years@MartijnPieters, I'm not sure I understand your point. The GitHub link you posted shows that
decimal.Decimal
was registered as a subclass ofNumber
in 2009, but PEP 3141 was written in 2007. So I'm not sure how you can argue thatdecimal.Decimal
was registered as aNumber
prior to the PEP being written and accepted. And the very first draft ofnumbers.Number
shows the module was only created in response to the acceptance of PEP 3141: github.com/python/cpython/commit/… -
Alex Waygood over 2 yearsI'm aware that
Decimal
is not a virtual subclass ofnumbers.Real
, but user1475412 also said: "Decimal
is not actually [...] part of the numeric tower at all", paraphrasing PEP 3141. I interpreted that line in the PEP to mean that the authors of the PEP did not intend forDecimal
to be registered as a virtual subclass of any of thenumbers
classes at that point in time. Am I wrong in that interpretation? -
bluenote10 over 2 years@AlexWaygood While that statement is true in a majority of cases, it is wrong to say that the type union in pointless in general.
int
andfloat
are different things in general, and if you code needs to differentiate between these differences locally, the type union is appropriate. Look e.g. atdir(1)
vsdir(1.0)
. If you need any of the member functions that are defined on either one, you need the type union so that the code typechecks correctly. -
bluenote10 over 2 yearsImho for the type checking of the function locally it can still make sense to use an explicit
Union[int, float]
. In general,int
andfloat
are different entities, and if the local code needs to differentiate between the two, the type union can be useful. Look e.g. atdir(1)
vsdir(1.0)
. If you need any of the member functions that are int/float specific, the explicit type union makes sense for type-checking the code locally. -
Alex Waygood over 2 years@bluenote10 thanks for the correction; you're right, my comment was too strongly worded.