-
-
Notifications
You must be signed in to change notification settings - Fork 12.2k
ENH: Make numpy scalars subtypes of their builtin counterpart during type checking #17105
Description
This issue proposes making np.generics subclasses of their respective builtin counterpart during the process type checking.
Numpy has a number of np.generic subclasses which is serve as counterpart to certain builtin objects (e.g. builtins.int & np.int64) and whenever a builtin scalar is expected it can generally be substituted for its numpy-based counterpart.
One of the situations where things become more difficult is static typing, as this requires explicit specifying both classes as valid types, lest a passed object might be rejected static type checkers such as mypy:
In [1]: import numpy as np
...: from typing import Union
In [2]: def func(a: Union[int, np.int64]) -> None: # This can quickly get rather ugly
...: pass
...:#17096 was created in order to make this process a bit smoother; so that the same Unions don't have to be redefined every single time. However, a simpler solution (which I'm proposing here instead) would be to make certain np.generic subclasses a subclass of their respective builtin counterpart for the purpose of type checking (this will not affect the classes' runtime behavior).
Note that mypy already does something similar for the buitin numerical types, .e.g. it considers int a subclass of floatandfloata subclass ofcomplex` (even though this is, strictly speaking, incorrect).
This approach has the following advantages:
- It improves general readability:
Union[cls_builtin, cls_np]simply becomescls_builtin. - It takes advantage of the fact that, generally speaking, builtin scalars can be substituted for their
np.generic-based counterpart without issue. - It will substantially increase the compatibility of numpy scalars with functions as typed by third-parties. To illustrate this point the example below provides a very reasonable annotated function; yet (in its current state) the likes of
mypywill raise an error here.
In [1]: import numpy as np
In [2]: def func(a: int) -> int:
...: return a * 2
...:
In [3]: func(np.int64(1)) # mypy(error): Argument 1 to "func" has incompatible type "int64"; expected "int"
Out[3]: 3Example Implementation
class complexfloating(np.inexact, complex): ...
...
class str_(np.character, str):
...