Currently, numpy.typing.NDArray only accepts a dtype, like so: numpy.typing.NDArray[numpy.int32]. You have some options though.

Use typing.Annotated

typing.Annotated allows you to create an alias for a type and to bundle some extra information with it.

In some my_types.py you would write all variations of shapes you want to hint:

from typing import Annotated, Literal, TypeVar
import numpy as np
import numpy.typing as npt


DType = TypeVar("DType", bound=np.generic)

Array4 = Annotated[npt.NDArray[DType], Literal[4]]
Array3x3 = Annotated[npt.NDArray[DType], Literal[3, 3]]
ArrayNxNx3 = Annotated[npt.NDArray[DType], Literal["N", "N", 3]]

And then in foo.py, you can supply a numpy dtype and use them as typehint:

import numpy as np
from my_types import Array4


def foo(arr: Array4[np.int32]):
    assert arr.shape == (4,)

MyPy will recognize arr to be an np.ndarray and will check it as such. The shape checking can be done at runtime only, like in this example with an assert.

If you don't like the assertion, you can use your creativity to define a function to do the checking for you.

def assert_match(arr, array_type):
    hinted_shape = array_type.__metadata__[0].__args__
    hinted_dtype_type = array_type.__args__[0].__args__[1]
    hinted_dtype = hinted_dtype_type.__args__[0]
    assert np.issubdtype(arr.dtype, hinted_dtype), "DType does not match"
    assert arr.shape == hinted_shape, "Shape does not match"


assert_match(some_array, Array4[np.int32])

Use nptyping

Another option would be to use 3th party lib nptyping (yes, I am the author).

You would drop my_types.py as it would be of no use anymore.

Your foo.py would become something like:

from nptyping import NDArray, Shape, Int32


def foo(arr: NDArray[Shape["4"], Int32]):
    assert isinstance(arr, NDArray[Shape["4"], Int32])

Use beartype + typing.Annotated

There is also another 3th party lib called beartype that you could use. It can take a variant of the typing.Annotated approach and will do the runtime checking for you.

You would reinstate your my_types.py with content similar to:

from beartype import beartype
from beartype.vale import Is
from typing import Annotated
import numpy as np


Int32Array4 = Annotated[np.ndarray, Is[lambda array:
    array.shape == (4,) and np.issubdtype(array.dtype, np.int32)]]
Int32Array3x3 = Annotated[np.ndarray, Is[lambda array:
    array.shape == (3,3) and np.issubdtype(array.dtype, np.int32)]]

And your foo.py would become:

import numpy as np
from beartype import beartype
from my_types import Int32Array4 


@beartype
def foo(arr: Int32Array4):
    ...  # Runtime type checked by beartype.

Use beartype + nptyping

You could also stack up both libraries.

Your my_types.py can be removed again and your foo.py would become something like:

from nptyping import NDArray, Shape, Int32
from beartype import beartype


@beartype
def foo(arr: NDArray[Shape["4"], Int32]):
    ...  # Runtime type checked by beartype.
Answer from R H on Stack Overflow
Top answer
1 of 3
71

Currently, numpy.typing.NDArray only accepts a dtype, like so: numpy.typing.NDArray[numpy.int32]. You have some options though.

Use typing.Annotated

typing.Annotated allows you to create an alias for a type and to bundle some extra information with it.

In some my_types.py you would write all variations of shapes you want to hint:

from typing import Annotated, Literal, TypeVar
import numpy as np
import numpy.typing as npt


DType = TypeVar("DType", bound=np.generic)

Array4 = Annotated[npt.NDArray[DType], Literal[4]]
Array3x3 = Annotated[npt.NDArray[DType], Literal[3, 3]]
ArrayNxNx3 = Annotated[npt.NDArray[DType], Literal["N", "N", 3]]

And then in foo.py, you can supply a numpy dtype and use them as typehint:

import numpy as np
from my_types import Array4


def foo(arr: Array4[np.int32]):
    assert arr.shape == (4,)

MyPy will recognize arr to be an np.ndarray and will check it as such. The shape checking can be done at runtime only, like in this example with an assert.

If you don't like the assertion, you can use your creativity to define a function to do the checking for you.

def assert_match(arr, array_type):
    hinted_shape = array_type.__metadata__[0].__args__
    hinted_dtype_type = array_type.__args__[0].__args__[1]
    hinted_dtype = hinted_dtype_type.__args__[0]
    assert np.issubdtype(arr.dtype, hinted_dtype), "DType does not match"
    assert arr.shape == hinted_shape, "Shape does not match"


assert_match(some_array, Array4[np.int32])

Use nptyping

Another option would be to use 3th party lib nptyping (yes, I am the author).

You would drop my_types.py as it would be of no use anymore.

Your foo.py would become something like:

from nptyping import NDArray, Shape, Int32


def foo(arr: NDArray[Shape["4"], Int32]):
    assert isinstance(arr, NDArray[Shape["4"], Int32])

Use beartype + typing.Annotated

There is also another 3th party lib called beartype that you could use. It can take a variant of the typing.Annotated approach and will do the runtime checking for you.

You would reinstate your my_types.py with content similar to:

from beartype import beartype
from beartype.vale import Is
from typing import Annotated
import numpy as np


Int32Array4 = Annotated[np.ndarray, Is[lambda array:
    array.shape == (4,) and np.issubdtype(array.dtype, np.int32)]]
Int32Array3x3 = Annotated[np.ndarray, Is[lambda array:
    array.shape == (3,3) and np.issubdtype(array.dtype, np.int32)]]

And your foo.py would become:

import numpy as np
from beartype import beartype
from my_types import Int32Array4 


@beartype
def foo(arr: Int32Array4):
    ...  # Runtime type checked by beartype.

Use beartype + nptyping

You could also stack up both libraries.

Your my_types.py can be removed again and your foo.py would become something like:

from nptyping import NDArray, Shape, Int32
from beartype import beartype


@beartype
def foo(arr: NDArray[Shape["4"], Int32]):
    ...  # Runtime type checked by beartype.
2 of 3
25

EDIT: Shape typing has been added to numpy. Types are covariant and they must be tuple[int, ...]. This allows things like NamedTuple, tuple[Literal[1]], tuple[NewType("Frame", int)]. Your example it would be:

import numpy as np
from typing import Literal

def foo(x: np.ndarray[tuple[Literal[4]], np.dtype[np.int32]]):

Currently, shape type information is reflected in ndarray.shape. However, most numpy functions that change the dimension or size of an array, however, don't necessarily know how to handle different axes and sizes in typing. As a result something like: arr[:] will lose the shape type information from arr.

Old answer: Prior to numpy 2.1, you could put anything as the first type argument to np.ndarray[_ShapeLike, _DtypeLike_co]. There was some consideration of TypeVarTuple (see PEP 646) for array shapes. However, bounds and variance is not implemented for TypeVarTuple, and therefore it cannot currently be used as the shape parameter.

🌐
NumPy
numpy.org › devdocs › reference › typing.html
Typing (numpy.typing) — NumPy v2.6.dev0 Manual
A np.ndarray[tuple[Any, ...], np.dtype[ScalarT]] type alias generic w.r.t. its dtype.type. Can be used during runtime for typing arrays with a given dtype and unspecified shape.
Discussions

Typing support for shapes
See how contracts package are trying to provide support for something similar to shapes. They are extending annotations in a different way than just standard typing and maybe something like that co... More on github.com
🌐 github.com
55
December 6, 2017
What the latest and greatest way to type hint numpy arrays?
I am aware of two ways: numpy.ndarray[tuple[int, int], float] means rank 2 matrix of floats. Does not distinguish between a shape of (2, 3) and (7, 5) - just enforces that the shape be length 2. numpy.typing.NDArray[float] must contain floats, shape not specified. I have heard of the ability to do numpy.ndarray[tuple[typing.Literal[4]], float] to say an array of floats of shape (4,), but I have not tried it and have heard that support is lacking. More on reddit.com
🌐 r/learnpython
7
3
December 14, 2025
Numpydantic - array typing and validation for pydantic and beyond
Hello everyone! @stefanv recommended i share this here, hopefully y’all find it interesting! I just released a package numpydantic that provides generic typing for array shape and dtype validation using pydantic (and also generally), post here: Dr. jonny phd: "Here's an ~ official ~ release ... More on discuss.scientific-python.org
🌐 discuss.scientific-python.org
1
2
May 25, 2024
ENH: numpy.typing for type checking, documentation and Numpy compilers
Proposed new feature or change: Type annotations in code using Numpy (and other Python array libraries) can be used for 3 purposes: Python-Numpy compilers (for example Cython or Pythran) documentat... More on github.com
🌐 github.com
2
May 3, 2024
Top answer
1 of 7
124

There is a numpy.typing module with an NDArray generic type.


From the Numpy 2.3 docs:
numpy.typing.NDArray = numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[~_ScalarT]]

A np.ndarray[tuple[Any, ...], np.dtype[ScalarT]] type alias generic w.r.t. its dtype.type.

Can be used during runtime for typing arrays with a given dtype and unspecified shape.

Examples:

>>> import numpy as np
>>> import numpy.typing as npt

>>> print(npt.NDArray)
numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[~_ScalarT]]

>>> print(npt.NDArray[np.float64])
numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float64]]

>>> NDArrayInt = npt.NDArray[np.int_]
>>> a: NDArrayInt = np.arange(10)

>>> def func(a: npt.ArrayLike) -> npt.NDArray[Any]:
...     return np.array(a)

As of 2025-07-17, support for shapes is still a work in progress per numpy/numpy#16544.

2 of 7
75

Update

Check recent numpy versions for a new typing module

https://numpy.org/doc/stable/reference/typing.html#module-numpy.typing

dated answer

It looks like typing module was developed at:

https://github.com/python/typing

The main numpy repository is at

https://github.com/numpy/numpy

Python bugs and commits can be tracked at

http://bugs.python.org/

The usual way of adding a feature is to fork the main repository, develop the feature till it is bomb proof, and then submit a pull request. Obviously at various points in the process you want feedback from other developers. If you can't do the development yourself, then you have to convince someone else that it is a worthwhile project.

cython has a form of annotations, which it uses to generate efficient C code.


You referenced the array-like paragraph in numpy documentation. Note its typing information:

A simple way to find out if the object can be converted to a numpy array using array() is simply to try it interactively and see if it works! (The Python Way).

In other words the numpy developers refuse to be pinned down. They don't, or can't, describe in words what kinds of objects can or cannot be converted to np.ndarray.

In [586]: np.array({'test':1})   # a dictionary
Out[586]: array({'test': 1}, dtype=object)

In [587]: np.array(['one','two'])  # a list
Out[587]: 
array(['one', 'two'], 
      dtype='<U3')

In [589]: np.array({'one','two'})  # a set
Out[589]: array({'one', 'two'}, dtype=object)

For your own functions, an annotation like

def foo(x: np.ndarray) -> np.ndarray:

works. Of course if your function ends up calling some numpy function that passes its argument through asanyarray (as many do), such an annotation would be incomplete, since your input could be a list, or np.matrix, etc.


When evaluating this question and answer, pay attention to the date. 484 was a relatively new PEP back then, and code to make use of it for standard Python still in development. But it looks like the links provided are still valid.

🌐
PyPI
pypi.org › project › nptyping
nptyping · PyPI
🧊 Type hints for NumPy 🐼 Type hints for pandas.DataFrame 💡 Extensive dynamic type checks for dtypes shapes and structures 🚀 Jump to the Quickstart ... >>> from nptyping import DataFrame, Structure as S >>> df: DataFrame[S["name: Str, x: Float, y: Float"]] ... >>> import numpy as np >>> isinstance(np.array([[1, 2], [3, 4]]), NDArray[Shape["2, 2"], Int]) True >>> isinstance(np.array([[1., 2.], [3., 4.]]), NDArray[Shape["2, 2"], Int]) False >>> isinstance(np.array([1, 2, 3, 4]), NDArray[Shape["2, 2"], Int]) False
      » pip install nptyping
    
Published: Feb 20, 2023
Version: 2.5.0
🌐
Taoa
taoa.io › posts › Shape-typing-numpy-with-pyright-and-variadic-generics
Shape typing numpy with pyright and variadic generics - T·A·O·A
February 27, 2023 - 1class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): We can see that it looks like numpy uses a Shape type already! But unfortunately if we look at the definition for this ... 1# TODO: Set the `bound` to something more suitable once we ... Luckily for us, we don't have to wait for shape support in numpy. PEP 646 has the base foundation for shape typing and has already been accepted into python==3.11!
🌐
Jim Fisher
jameshfisher.com › 2024 › 04 › 12 › shape-typing-in-python
Shape typing in Python - Jim Fisher
April 12, 2024 - This uses Numpy’s np.ndarray type, which takes two arguments that describe the shape and dtype. For example, we can describe a 2x3 matrix of integers as: mat2x3: np.ndarray[ tuple[Literal[2], Literal[3]], np.dtype[np.int64], ] = ...
🌐
GitHub
github.com › numpy › numpy › issues › 16544
Typing support for shapes · Issue #16544 · numpy/numpy
December 6, 2017 - They are extending annotations in a different way than just standard typing and maybe something like that could be also done. So instead of providing specific extension (PEP) for typing to allow thing like shapes, it might be maybe more useful to determine a syntax for general constraints on types and use that, in addition to standard types through typing.
Author: numpy
Find elsewhere
🌐
Medium
medium.com › data-science-collective › do-more-with-numpy-array-type-hints-annotate-validate-shape-dtype-09f81c496746
Do More with NumPy Array Type Hints: Annotate & Validate Shape & Dtype | by Christopher Ariza | Data Science Collective | Medium
May 26, 2025 - Thus, a tuple[int] can specify a 1D array; a tuple[int, int, int] can specify a 3D array; a tuple[int, …], specifying a tuple of zero or more integers, denotes an N-dimensional array. It might be possible in the future to type-check an np.ndarray with specific magnitudes per dimension (using Literal), but this is not yet broadly supported. The NumPy dtype object defines element types and, for some types, other characteristics such as size (for Unicode and string types) or unit (for np.datetime64 types).
🌐
W3Schools
w3schools.com › python › numpy › numpy_array_shape.asp
NumPy Array Shape
import numpy as np arr = np.array([1, 2, 3, 4], ndmin=5) print(arr) print('shape of array :', arr.shape) Try it Yourself »
🌐
Scientific Python
discuss.scientific-python.org › contributor & development discussion
Numpydantic - array typing and validation for pydantic and beyond - Contributor & Development Discussion - Scientific Python
May 25, 2024 - Hello everyone! @stefanv recommended i share this here, hopefully y’all find it interesting! I just released a package numpydantic that provides generic typing for array shape and dtype validation using pydantic (and also generally), post here: Dr. jonny phd: "Here's an ~ official ~ release announcement for #…" - Neuromatch Social The idea is that we not only want to be able to specify arrays in pydantic, specify shape and dtype constraints for those arrays, but also be able to ...
🌐
w3resource
w3resource.com › numpy › snippet › exploring-numpy-typing.php
Exploring numpy.typing for Enhanced Type Hints
import numpy as np from numpy.typing import NDArray # Define a function to perform element-wise square def square_elements(arr: NDArray[np.int32]) -> NDArray[np.int32]: return arr ** 2 # Test the function array = np.array([1, 2, 3], dtype=np.int32) squared_array = square_elements(array) print("Original array:", array) print("Squared array:", squared_array) ... Both the input and output of the function are strictly typed, ensuring consistency. ... 1. Performance: Type hints do not affect runtime performance. They are ignored by the Python interpreter. 2. Backward Compatibility: The numpy.typing module is available from NumPy 1.21.0.
🌐
Like Geeks
likegeeks.com › home › python › python type hinting for numpy arrays
Python Type Hinting for NumPy Arrays
The function normalizes the array so that it has a mean of 0 and a standard deviation of 1. You can combine shape and dtype for more precise type hints.
🌐
NumPy
numpy.org › doc › 2.3 › reference › typing.html
Typing (numpy.typing) — NumPy v2.3 Manual
The timedelta64 class is not considered a subclass of signedinteger, the former only inheriting from generic while static type checking. During runtime numpy aggressively casts any passed 0D arrays into their corresponding generic instance. Until the introduction of shape typing (see PEP 646) it is unfortunately not possible to make the necessary distinction between 0D and >0D arrays.
🌐
GitHub
github.com › numpy › numpy › issues › 26380
ENH: numpy.typing for type checking, documentation and Numpy compilers · Issue #26380 · numpy/numpy
May 3, 2024 - I'm not saying that numpy.typing should support such things but it seems to me that it is important when designing numpy.typing to consider the different purposes of type annotations in code using Numpy and not to be mostly focus on what is currently supported by Mypy. Simple things like specifying that an array is a one or two-dimensional array of float64 should be simple and short with numpy.typing.
Author: numpy
🌐
NumPy
numpy.org › doc › stable › reference › typing.html
Typing (numpy.typing) — NumPy v2.5 Manual
A np.ndarray[tuple[Any, ...], np.dtype[ScalarT]] type alias generic w.r.t. its dtype.type. Can be used during runtime for typing arrays with a given dtype and unspecified shape.
🌐
Reddit
reddit.com › r/python › tutorial on type hinting any matrix with protocol (compatible with numpy and nested lists)
r/Python on Reddit: Tutorial on type hinting any matrix with Protocol (compatible with numpy and nested lists)
October 3, 2024 -

I went down a rabbit hole trying to find the perfect way to type hint a matrix. Here's what I learned. First, the naive approach:

matrix3x3: list[list[int]] = [[1,2,3],[4,5,6],[7,8,9]]

There are two problems with this. The first is that list[list[int]] is a concrete type, and we'd like it to be abstract. As is, mypy would raise an error if we tried to do this:

import numpy as np
matrix3x3 = np.ndarray(shape=(3, 3), buffer=np.array([[1,2,3],[4,5,6],[7,8,9]])) # error

We would like to be able to do this though, because an NDArray shares all the relevant qualities of a matrix for our application.

The second problem is more subtle. matrix3x3 is meant to always be 3x3, but Python's lists are dynamically resizable, which means the shape can be tampered with. Ideally, we'd like mypy to be able to raise an error before runtime if someone else later tries to write matrix3x3.pop() or matrix3x3[0].append(something). This is not a problem in a language like Java, where Arrays are fixed-size.

There are three ways around these issues:

1. Switch to a statically-typed language.

This is the least preferable option, but something everyone should consider if they keep resisting duck typing. I still prefer duck typing at least for prototyping.

2. Modify the implementation.

This is certainly better, but not the best option. It's worth demonstrating how you could do this. For example, we can start with this:

class FixedShapeMatrix:
  def __init__(rows: int, cols: int) -> None:
    _matrix = [[0 for c in cols] for r in rows]

and continue defining the functionality of the FixedShapeMatrix object so that it has an immutable shape with mutable entries.

Another example is to just use numpy instead:

import numpy as np
from numpy import typing as npt

matrix3x3: npt.NDArray[np.int64] = np.ndarray((3,3), buffer=np.array([[1,2,3],[4,5,6],[7,8,9]])

Both of these solutions suffer from the same problem: they require significant refactoring of the existing project. And even if you had the time, you will lose generality when you pick the NDArray or FixedShapeMatrix implementations. Ideally, you want matrix3x3 to be structurally typed such that any of these implementations can be assigned to it. When you pigeonhole your matrix3x3 type, you lose the Abstraction of OOP. Thankfully, with Protocol, there's another way.

3. Structural subtyping.

Note: I'm going to be using Python 3.12 typing notation. As a quick reference, this is code in 3.11:

from typing import TypeVar, Generic

T = TypeVar('T', bound=int|float)


class MyClass(Generic[T]):

  def Duplicates(self, val: T) -> list[T]:
    return [val] * 2

And this is the same code in 3.12 (no imports needed):

class MyClass[T: int|float]:

  def Duplicates(self, val: T) -> list[T]:
    return [val] * 2

So, let's finally try to make an abstract matrix type directly. I'm going to show you how I iteratively figured it out. If you're already a little familiar with Protocol, you might have guessed this:

type Matrix[T] = Sequence[Sequence[T]]

But the problem is that Sequence is read-only. We're going to have to create our own type from scratch. The best way to start is to realize which methods we really need from the matrix:

  1. indexing (read + write)

  2. iterable

  3. sized

The first attempt might be this:

from typing import Protocol


class Matrix(Protocol):

  def __getitem__(): ...

  def __setitem__(): ...

  def __len__(): ...

  def __iter__(): ...

But there are multiple problems with this. The first is that we need to explicitly annotate the types of each of these functions, or else our matrix won't be statically hinted.

from typing import Protocol, Iterator


class Matrix(Protocol):

  def __getitem__(self, index: int) -> int | Matrix: ...

  def __setitem__(self, index: int, val: int | Matrix) -> None: ...

  def __len__(self) -> int: ...

  def __iter__(self) -> Iterator[int | Matrix]: ...

The idea here is that matrix3x3[0][0] is an int, while the type of matrix3x3[0] is recursively a matrix that contains ints. But this doesn't protect against matrix3x3: Matrix = [1,2,3,[4,5,6],7,8,9] , which is not a matrix.

Here we realize that we should handle the internal rows as their own type.

from typing import Protocol, Iterator


class MatrixRow(Protocol):

  def __getitem__(self, index: int) -> int: ...

  def __setitem__(self, index: int, value: int) -> None: ...

  def __len__(self) -> int: ...

  def __iter__(self) -> Iterator[int]: ...


class Matrix(Protocol):

  def __getitem__(self, index: int) -> MatrixRow: ...

  def __setitem__(self, index: int, value: MatrixRow) -> None: ...

  def __len__(self) -> int: ...

  def __iter__(self) -> Iterator[MatrixRow]: ...

Now both the matrix and its rows are iterable, sized, and have accessible and mutable indexes.

matrix3x3: Matrix = [[1,2,3],[4,5,6],[7,8,9]] # good
matrix3x3.append([10,11,12]) # error - good!
matrix3x3[0][2] = 10 # good
matrix3x3[0][0] += 1 # good
matrix3x3[1].append(7) # error - good!

There's just one bug though. See if you can find it first:

matrix3x3[1] = [4,5,6,7] # no error - bad!

The solution is we need to remove __setitem__ from Matrix. We will still be able to modify the elements of any MatrixRow without it. Bonus points if you understand why (hint: references).

So let's go ahead and do that, and as a final touch, let's make it so that the matrix values all must have the same type. To do this, we enforce a generic type that supports integer operations (int, float, np.int32, np.float64, etc). Here's how I did that:

from typing import Protocol, Iterator, SupportsInt


class MatrixRow[T: SupportsInt](Protocol):

  def __getitem__(self, index: int) -> T: ...

  def __setitem__(self, index: int, value: T) -> None: ...

  def __len__(self) -> int: ...

  def __iter__(self) -> Iterator[T]: ...


class Matrix[S: SupportsInt](Protocol):

  def __getitem__(self, index: int) -> MatrixRow[S]: ...

  def __len__(self) -> int: ...

  def __iter__(self) -> Iterator[MatrixRow[S]]: ...

Now all of these work!

matrix3x3: Matrix[int]
matrix3x3 = [[1,2,3],[4,5,6],[7,8,9]]
matrix3x3 = np.array([[1,2,3],[4,5,6],[7,8,9]])
matrix3x3 = np.ndarray(shape=(3, 3), buffer=np.array([[1,2,3],[4,5,6],[7,8,9]]))
for row in matrix3x3:
  for val in row:
    print(val)
print(len(matrix3x3), len(matrix3x3[0]))

And all of these raise errors!

matrix3x3.append([10,11,12])
matrix3x3[2].append(10)
matrix3x3.pop()
matrix3x3[0].pop()
matrix3x3[0][0] = "one"

And even if some of those implementations are intrinsically mutable in size, our type system lets mypy catch any bug where matrix3x3 is reshaped. Unfortunately, there's no way to prevent someone from assigning a 4x7 matrix to matrix3x3, but at least it's named clearly. Maybe someday there will be Python support for fixed-size lists as types.

🌐
Python Like You Mean It
pythonlikeyoumeanit.com › Module5_OddsAndEnds › Writing_Good_Code.html
Writing Good Code — Python Like You Mean It
Eventually, popular 3rd party libraries like NumPy will contribute their own typing modules so that you can provide higher-fidelity hints that indicate things like data-type and array-shape. NumPy developers are currently working on this. ... Python’s typing module contains objects that are used to create descriptive type-hints.
🌐
Cython
cython.readthedocs.io › en › latest › src › tutorial › numpy.html
Working with NumPy — Cython 3.3.0 documentation
All other use (attribute lookup or indexing) can potentially segfault or corrupt data (rather than raising exceptions as they would in Python). The actual rules are a bit more complicated but the main message is clear: Do not use typed objects without knowing that they are not set to None. The main purpose of typing things as ndarray is to allow efficient indexing of single elements, and to speed up access to a small number of attributes such as .shape. Typing does not allow Cython to speed up mathematical operations on the whole array (for example, adding two arrays together).