How to type hint a generic numeric type in Python?
Question:
Forgive me if this question has been asked before but I could not find any related answer.
Consider a function that takes a numerical type as input parameter:
def foo(a):
return ((a+1)*2)**4;
This works with integers, floats and complex numbers.
Is there a basic type so that I can do a type hinting (of a real existing type/base class), such as:
def foo(a: numeric):
return ((a+1)*2)**4;
Furthermore I need to use this in a collection type parameter, such as:
from typing import Collection;
def foo(_in: Collection[numeric]):
return ((_in[0]+_in[1])*2)**4;
Answers:
There isn’t a generic numeric type in the typing
module, so you would have to create such a type with Union
instead:
from typing import Union
numeric = Union[int, float, complex]
...
To add support for Numpy’s collection of numeric types, add np.number
to that Union.
numeric = Union[int, float, complex, np.number]
PEP 3141 added abstract base classes for numbers, so you could use:
from numbers import Number
def foo(a: Number) -> Number:
...
The currently accepted solution of using Number
is fairly broken considering that, as pointed out in the comments, int
s are not Number
s for static type checkers like mypy and PyRight. The situation has been discussed for years with no clear resolution.
Another possible approach extracted from a detailed explanation from a related question is:
from typing import SupportsFloat as Numeric
which has the following behavior:
from decimal import Decimal
from fractions import Fraction
from typing import SupportsFloat as Numeric
import numpy as np
def f(x: Numeric) -> None:
pass
# Accepted by mypy/Pyright:
f(123)
f(np.uintc(55))
f(Fraction(-3, 2))
f(Decimal("-3.14"))
f(np.array([1, 2, 3])) # Should an array be numeric?
# Results in type errors:
f(complex(2, 3))
f("asdf")
This has the advantage of being fairly permissive, except for complex
. In case you want to include complex
as well, simply do
from typing import SupportsFloat, Union
Numeric = Union[SupportsFloat, complex]
or equivalently in Python ≥3.10 style:
from typing import SupportsFloat, TypeAlias
Numeric: TypeAlias = SupportsFloat | complex
It’s perhaps unfortunate that NumPy arrays are considered numeric in the sense of SupportsFloat
, but that illustrates the obscure philosophical nature of the question "what is a number?".
Major disadvantage:
As noted by @lkwbr, the SupportsFloat
type is only really suitable for identifying numbers. It fails to support any operators like addition or comparison.
For instance, Pyright gives the following error:
Operator "<" not supported for types "SupportsFloat" and "SupportsFloat"
Forgive me if this question has been asked before but I could not find any related answer.
Consider a function that takes a numerical type as input parameter:
def foo(a):
return ((a+1)*2)**4;
This works with integers, floats and complex numbers.
Is there a basic type so that I can do a type hinting (of a real existing type/base class), such as:
def foo(a: numeric):
return ((a+1)*2)**4;
Furthermore I need to use this in a collection type parameter, such as:
from typing import Collection;
def foo(_in: Collection[numeric]):
return ((_in[0]+_in[1])*2)**4;
There isn’t a generic numeric type in the typing
module, so you would have to create such a type with Union
instead:
from typing import Union
numeric = Union[int, float, complex]
...
To add support for Numpy’s collection of numeric types, add np.number
to that Union.
numeric = Union[int, float, complex, np.number]
PEP 3141 added abstract base classes for numbers, so you could use:
from numbers import Number
def foo(a: Number) -> Number:
...
The currently accepted solution of using Number
is fairly broken considering that, as pointed out in the comments, int
s are not Number
s for static type checkers like mypy and PyRight. The situation has been discussed for years with no clear resolution.
Another possible approach extracted from a detailed explanation from a related question is:
from typing import SupportsFloat as Numeric
which has the following behavior:
from decimal import Decimal
from fractions import Fraction
from typing import SupportsFloat as Numeric
import numpy as np
def f(x: Numeric) -> None:
pass
# Accepted by mypy/Pyright:
f(123)
f(np.uintc(55))
f(Fraction(-3, 2))
f(Decimal("-3.14"))
f(np.array([1, 2, 3])) # Should an array be numeric?
# Results in type errors:
f(complex(2, 3))
f("asdf")
This has the advantage of being fairly permissive, except for complex
. In case you want to include complex
as well, simply do
from typing import SupportsFloat, Union
Numeric = Union[SupportsFloat, complex]
or equivalently in Python ≥3.10 style:
from typing import SupportsFloat, TypeAlias
Numeric: TypeAlias = SupportsFloat | complex
It’s perhaps unfortunate that NumPy arrays are considered numeric in the sense of SupportsFloat
, but that illustrates the obscure philosophical nature of the question "what is a number?".
Major disadvantage:
As noted by @lkwbr, the SupportsFloat
type is only really suitable for identifying numbers. It fails to support any operators like addition or comparison.
For instance, Pyright gives the following error:
Operator "<" not supported for types "SupportsFloat" and "SupportsFloat"