I'm trying to type hint a numpy ndarray like this:
RGB = numpy.dtype[numpy.uint8] ThreeD = tuple[int, int, int] def load_images(paths: list[str]) -> tuple[list[numpy.ndarray[ThreeD, RGB]], list[str]]: ... but at the first line when I run this, I got the following error:
RGB = numpy.dtype[numpy.uint8] TypeError: 'numpy._DTypeMeta' object is not subscriptable How do I type hint a ndarray correctly?
4 Answers
I had a similar issue with my opencv2 library, which I by upgrading numpy pip install numpy --upgrade
pip install numpy==1.20.0 will work
It turns out that strongly type a numpy array is not straightforward at all. I spent a couple of hours to figure out how to do it properly.
A simple method that do not add yet another dependency to your project is to use a trick described here. Just wrap numpy types with with ':
import numpy import numpy.typing as npt from typing import cast, Type, Sequence import typing RGB: typing.TypeAlias = 'numpy.dtype[numpy.uint8]' ThreeD: typing.TypeAlias = tuple[int, int, int] NDArrayRGB: typing.TypeAlias = 'numpy.ndarray[ThreeD, RGB]' def load_images(paths: list[str]) -> tuple[list[NDArrayRGB], list[str]]: ... The trick is to use single-quotes to avoid the infamous TypeError: 'numpy._DTypeMeta' object is not subscriptable when Python tries to interpret the [] in the expression. This trick is well handled for instance by VSCode Pylance type-checker:
Notice that the colors for types are respected and that the execution gives no error.
Note about nptyping
As suggested by @ddejohn, one can use nptyping. Just install the package: pip install nptyping. However, as of now (16 June 2022), there is no Tuple type defined in nptyping so you won't be able to prefectly type you code that way. I have open a new issue so maybe in the future it will work.
edits
Turns out there is a different way to express a tuple as a nptyping.Shape as answered by ramonhagenaars, which is also elegant:
from nptyping import NDArray, Shape, UInt8 # A 1-dimensional array (i.e. 1 RGB color). RGBArray1D = NDArray[Shape["[r, g, b]"], UInt8] # A 2-dimensional array (i.e. an array of RGB colors). RGBArrayND = NDArray[Shape["*, [r, g, b]"], UInt8] def load_images_trick(paths: list[str]) -> tuple[list[RGBArrayND], list[str]]: ... However, this solution is not well supported by VSCode Pylance, an I get an error suggestion for Shape:
Expected class type but received "Literal" "Literal" is not a class "Literal" is not a classPylancereportGeneralTypeIssues Pylance(reportGeneralTypeIssues) 2The command np.ndarray[...] raises an exception for certain versions of numpy. The command, typing.TypeAlias used in the accepted answer also raises an exception for certain versions of python, e.g. [1]. I had this issue while collaborating with colleagues with different OS and different python versions. They updated numpy but the error persisted, so I came up with this solution that worked for all of us and still checks for type errors in my machine:
from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import TypeAlias else: TypeAlias = "TypeAlias" NDArrayInt: "TypeAlias" = "np.ndarray[int, np.dtype[np.number]]" NDArrayFloat: "TypeAlias" = "np.ndarray[float, np.dtype[np.number]]" DiscreteMechanism: "TypeAlias" = "Callable[[NDArrayInt], NDArrayInt]" 