mypy, type hint: Union[float, int] -> is there a Number type?

26,483

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 type float, an argument of type int 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
Share:
26,483

Related videos on Youtube

JPFrancoia
Author by

JPFrancoia

Updated on October 08, 2021

Comments

  • JPFrancoia
    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
    user1475412 about 5 years
    Tangentially related, but Decimal is not actually a subclass of Real, or part of the numeric tower at all: python.org/dev/peps/pep-3141/#the-decimal-type
  • EliadL
    EliadL about 3 years
    Good idea, but int is officially redundant when float is present. See this answer: stackoverflow.com/a/50928627/4960855
  • Alex Waygood
    Alex Waygood over 2 years
    As 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
    Alex Waygood over 2 years
    @user1475412 — interestingly, decimal.Decimal is now registered as a virtual subclass of numbers.Number (though still isn't a virtual subclass of numbers.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
    Martijn Pieters over 2 years
    @AlexWaygood: I think you misundrestood something; decimal.Decimal has been registered as a Number for 13 years now, well before the PEP. It still isn't a virtual subclass of numbers.Real, which is what user1475412 is talking about.
  • Alex Waygood
    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 of Number in 2009, but PEP 3141 was written in 2007. So I'm not sure how you can argue that decimal.Decimal was registered as a Number prior to the PEP being written and accepted. And the very first draft of numbers.Number shows the module was only created in response to the acceptance of PEP 3141: github.com/python/cpython/commit/…
  • Alex Waygood
    Alex Waygood over 2 years
    I'm aware that Decimal is not a virtual subclass of numbers.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 for Decimal to be registered as a virtual subclass of any of the numbers classes at that point in time. Am I wrong in that interpretation?
  • bluenote10
    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 and float are different things in general, and if you code needs to differentiate between these differences locally, the type union is appropriate. Look e.g. at dir(1) vs dir(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
    bluenote10 over 2 years
    Imho for the type checking of the function locally it can still make sense to use an explicit Union[int, float]. In general, int and float are different entities, and if the local code needs to differentiate between the two, the type union can be useful. Look e.g. at dir(1) vs dir(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
    Alex Waygood over 2 years
    @bluenote10 thanks for the correction; you're right, my comment was too strongly worded.