Currently, I am checking for tuples with multiple (e.g. three) numbers of arbitrary but equal type in the following form:
from typing import Tuple, Union
Union[Tuple[int, int, int], Tuple[float, float, float]]
I want to make this check more generic, also allowing numpy
number types. I.e. I tried to use numbers.Number
:
from numbers import Number
from typing import Tuple
Tuple[Number, Number, Number]
The above snipped also allows tuples of mixed types as long as everything is a number.
I'd like to restrict the tuple to numbers of equal type.
How can this be achieved?
Technically, this question applies to Python and the type hints specification itself. However, as pointed out in the comments, its handling is implementation specific, i.e. MyPy will not catch every edge case and/or inconsistency. Personally, I am using run-time checks with typeguard for testing and deactivate them entirely in production.
Finally, you'll see that it is also possible to assign multiple values at once to tuples.
A tuple can have any number of items and they may be of different types (integer, float, list, string, etc.). A tuple can also be created without using parentheses.
A tuple's elements do not have to be of the same type; they are separated by commas, and surrounded by parantheses: (), (3, "aha!"), (one, (two, three)). If the elements of a tuple have, successively, types t1, t2, ..., tn, then the type of the corresponding tuple is t1*t2* ...
A tuple is an immutable sequence data type. The tuple can contain mixed data types.
You can use TypeVar
with bound
argument. It allows restricting types to subtypes of a given type. In your case, the types should be the subtypes of Number
:
from numbers import Number
from typing import TypeVar
T = TypeVar('T', bound=Number)
Tuple[T, T, T]
TypeVar
is a variable that allows to use a particular type several times in type signatures. The simplest example:
from typing import TypeVar, Tuple
T = TypeVar('T')
R = TypeVar('R')
def swap(x: T, y: R) -> Tuple[R, T]:
return y, x
The static type checker will infer that arguments of the function swap
should be the same as outputs in reversed order (note that return type T will be the same as the input type T).
Next, it might be useful to introduce restrictions for TypeVar
. For instance, one could restrict values of a type variable to specific types: TypeVar('T', int, str)
. In this answer, we use another kind of restriction with a keyword bound
– it checks if the values of the type variable are subtypes of a given type (in our case, Number
, which is a base class for all numerical types in Python).
More examples: mypy documentation and PEP 484.
from numbers import Number
from typing import Tuple, TypeVar
import numpy as np
from typeguard import typechecked
T = TypeVar('T', bound=Number)
@typechecked
def foo(data: Tuple[T, T, T]):
print('expected OK:', data)
for data in (
(1, 2, 3), # ok
(1.0, 2.0, 3.0), # ok
(1, 2.0, 3), # TypeError
(1.0, 2.0, 3), # TypeError
(1.0, 2.0, np.float64(3.0)), # ok (yes!)
(1, 2.0, np.float32(3)), # TypeError
(1, 2.0, np.uint8(3)), # TypeError
):
try:
foo( data )
except TypeError:
print('expected TypeError:', data)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With