🤝 Like the tool? Help making it better! Your donation helps! 🤝

# Working with Python arrays

Note

This page uses two different syntax variants:

- Cython specific `cdef` syntax, which was designed to make type declarations concise and easily readable from a C/C++ perspective.

- Pure Python syntax which allows static Cython type declarations in pure Python code, following PEP-484 type hints and PEP 526 variable annotations.

  To make use of C data types in Python syntax, you need to import the special `cython` module in the Python module that you want to compile, e.g.

      import cython

  If you use the pure Python syntax we strongly recommend you use a recent Cython 3 release, since significant improvements have been made here compared to the 0.29.x releases.

Python has a builtin array module supporting dynamic 1-dimensional arrays of primitive types. It is possible to access the underlying C array of a Python array from within Cython. At the same time they are ordinary Python objects which can be stored in lists and serialized between processes when using `multiprocessing`.

Compared to the manual approach with `malloc()` and `free()`, this gives the safe and automatic memory management of Python, and compared to a Numpy array there is no need to install a dependency, as the `array` module is built into both Python and Cython.

## Safe usage with memory views

    from cython.cimports.cpython import array
    import array
    a = cython.declare(array.array, array.array('i', [1, 2, 3]))
    ca = cython.declare(cython.int[:], a)

    print(ca[0])

    from cpython cimport array
    import array
    cdef array.array a = array.array('i', [1, 2, 3])
    cdef int[:] ca = a

    print(ca[0])

NB: the import brings the regular Python array object into the namespace while the cimport adds functions accessible from Cython.

A Python array is constructed with a type signature and sequence of initial values. For the possible type signatures, refer to the Python documentation for the array module.

Notice that when a Python array is assigned to a variable typed as memory view, there will be a slight overhead to construct the memory view. However, from that point on the variable can be passed to other functions without overhead, so long as it is typed:

    from cython.cimports.cpython import array
    import array

    a = cython.declare(array.array, array.array('i', [1, 2, 3]))
    ca = cython.declare(cython.int[:], a)

    @cython.cfunc
    def overhead(a: cython.object) -> cython.int:
        ca: cython.int[:] = a
        return ca[0]

    @cython.cfunc
    def no_overhead(ca: cython.int[:]) -> cython.int:
        return ca[0]

    print(overhead(a))  # new memory view will be constructed, overhead
    print(no_overhead(ca))  # ca is already a memory view, so no overhead

    from cpython cimport array
    import array

    cdef array.array a = array.array('i', [1, 2, 3])
    cdef int[:] ca = a

    cdef int overhead(object a):
        cdef int[:] ca = a
        return ca[0]

    cdef int no_overhead(int[:] ca):
        return ca[0]

    print(overhead(a))  # new memory view will be constructed, overhead
    print(no_overhead(ca))  # ca is already a memory view, so no overhead

## Zero-overhead, unsafe access to raw C pointer

To avoid any overhead and to be able to pass a C pointer to other functions, it is possible to access the underlying contiguous array as a pointer. There is no type or bounds checking, so be careful to use the right type and signedness.

    from cython.cimports.cpython import array
    import array

    a = cython.declare(array.array, array.array('i', [1, 2, 3]))

    # access underlying pointer:
    print(a.data.as_ints[0])

    from cython.cimports.libc.string import memset

    memset(a.data.as_voidptr, 0, len(a) * cython.sizeof(cython.int))

    from cpython cimport array
    import array

    cdef array.array a = array.array('i', [1, 2, 3])

    # access underlying pointer:
    print(a.data.as_ints[0])

    from libc.string cimport memset

    memset(a.data.as_voidptr, 0, len(a) * sizeof(int))

Note that any length-changing operation on the array object may invalidate the pointer.

## Cloning, extending arrays

To avoid having to use the array constructor from the Python module, it is possible to create a new array with the same type as a template, and preallocate a given number of elements. The array is initialized to zero when requested.

    from cython.cimports.cpython import array
    import array

    int_array_template = cython.declare(array.array, array.array('i', []))
    cython.declare(newarray=array.array)

    # create an array with 3 elements with same type as template
    newarray = array.clone(int_array_template, 3, zero=False)

    from cpython cimport array
    import array

    cdef array.array int_array_template = array.array('i', [])
    cdef array.array newarray

    # create an array with 3 elements with same type as template
    newarray = array.clone(int_array_template, 3, zero=False)

An array can also be extended and resized; this avoids repeated memory reallocation which would occur if elements would be appended or removed one by one.

    from cython.cimports.cpython import array
    import array

    a = cython.declare(array.array, array.array('i', [1, 2, 3]))
    b = cython.declare(array.array, array.array('i', [4, 5, 6]))

    # extend a with b, resize as needed
    array.extend(a, b)
    # resize a, leaving just original three elements
    array.resize(a, len(a) - len(b))

    from cpython cimport array
    import array

    cdef array.array a = array.array('i', [1, 2, 3])
    cdef array.array b = array.array('i', [4, 5, 6])

    # extend a with b, resize as needed
    array.extend(a, b)
    # resize a, leaving just original three elements
    array.resize(a, len(a) - len(b))

## API reference

### Data fields

    data.as_voidptr
    data.as_chars
    data.as_schars
    data.as_uchars
    data.as_shorts
    data.as_ushorts
    data.as_ints
    data.as_uints
    data.as_longs
    data.as_ulongs
    data.as_longlongs  # requires Python >=3
    data.as_ulonglongs  # requires Python >=3
    data.as_floats
    data.as_doubles
    data.as_pyunicodes

Direct access to the underlying contiguous C array, with given type; e.g., `myarray.data.as_ints`.

### Functions

The following functions are available to Cython from the array module

    @cython.cfunc
    @cython.exceptval(-1)
    def resize(self: array.array, n: cython.Py_ssize_t) -> cython.int

    cdef int resize(array.array self, Py_ssize_t n) except -1

Fast resize / realloc. Not suitable for repeated, small increments; resizes underlying array to exactly the requested amount.

    @cython.cfunc
    @cython.exceptval(-1)
    def resize_smart(self: array.array, n: cython.Py_ssize_t) -> cython.int

    cdef int resize_smart(array.array self, Py_ssize_t n) except -1

Efficient for small increments; uses growth pattern that delivers amortized linear-time appends.

    @cython.cfunc
    @cython.inline
    def clone(template: array.array, length: cython.Py_ssize_t, zero: cython.bint) -> array.array

    cdef inline array.array clone(array.array template, Py_ssize_t length, bint zero)

Fast creation of a new array, given a template array. Type will be same as `template`. If zero is `True`, new array will be initialized with zeroes.

    @cython.cfunc
    @cython.inline
    def copy(self: array.array) -> array.array

    cdef inline array.array copy(array.array self)

Make a copy of an array.

    @cython.cfunc
    @cython.inline
    @cython.exceptval(-1)
    def extend_buffer(self: array.array, stuff: cython.p_char, n: cython.Py_ssize_t) -> cython.int

    cdef inline int extend_buffer(array.array self, char* stuff, Py_ssize_t n) except -1

Efficient appending of new data of same type (e.g. of same array type) `n`: number of elements (not number of bytes!)

    @cython.cfunc
    @cython.inline
    @cython.exceptval(-1)
    def extend(self: array.array, other: array.array) -> cython.int

    cdef inline int extend(array.array self, array.array other) except -1

Extend array with data from another array; types must match.

    @cython.cfunc
    @cython.inline
    def zero(self: array.array) -> cython.void

    cdef inline void zero(array.array self)

Set all elements of array to zero.