Vector ops without NumPy: array(‘d’) + memoryviews#

You can write fast numeric code without NumPy using Python’s array module and typed memoryviews.

Why this works#

array('d') exposes the buffer protocol, so Cython can view its memory as a contiguous double[:] without copying.

This example scales a vector in-place and demonstrates:

  • typed memoryviews (double[:])

  • strict input validation (buffer-compatible, correct itemsize)

  • optional compiler directives for speed (boundscheck/wraparound)

Notes#

This example does not require NumPy.

# Authors: The scikit-plots developers
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import annotations

from array import array
from typing import Iterable

from scikitplot import cython
def py_scale(x: array, a: float) -> None:
    """Pure-Python baseline: scale in-place."""
    for i in range(len(x)):
        x[i] *= a

Generate python Module from cython#

report = cython.check_build_prereqs(numpy=False)

if not report.get('cython', {}).get('ok'):
    print("Skipping compilation because build prerequisites are missing.")
    problems = report.get("problems", [])
    if problems:
        print("Problems:", problems)
else:
    code = r"""
# cython: language_level=3
# cython: boundscheck=False
# cython: wraparound=False

def scale(double[:] x, double a):
    '''Scale a 1D contiguous vector in-place.'''
    cdef Py_ssize_t i
    cdef Py_ssize_t n = x.shape[0]
    for i in range(n):
        x[i] *= a
    return None

def scaled_copy(double[:] x, double a):
    cdef Py_ssize_t i
    cdef Py_ssize_t n = x.shape[0]
    out = [0.0] * n
    for i in range(n):
        out[i] = x[i] * a
    return out
"""

    # Use result API so we can show cache metadata.
    r = cython.compile_and_load_result(
        code,
        module_name="memview_scale_demo",
        profile="fast-debug",
        numpy_support=False,
        verbose=0,
    )
    m = r.module

    print("Build key:", r.key)
    print("Artifact :", r.artifact_path)

    # ------------------------------------------------------------
    # Strict demo: array('d') is buffer-compatible with double[:]
    # ------------------------------------------------------------
    x = array("d", [1.0, 2.0, 3.0])

    # Educational sanity checks (strict, no heuristics).
    print("\nInput type:", type(x).__name__)
    print("Typecode   :", x.typecode)
    print("Length     :", len(x))

    # Run compiled scaling.
    m.scale(x, 10.0)
    print("After Cython scale:", list(x))
    print("Expected         :", [10.0, 20.0, 30.0])

    # ------------------------------------------------------------
    # Baseline: pure Python scaling (for learning)
    # ------------------------------------------------------------
    y = array("d", [1.0, 2.0, 3.0])
    py_scale(y, 10.0)
    print("\nAfter Python scale:", list(y))

    # ------------------------------------------------------------
    # Strict edge case: wrong typecode (will raise TypeError)
    # ------------------------------------------------------------
    z = array("f", [1.0, 2.0, 3.0])  # float32
    try:
        m.scale(z, 2.0)
        print("Unexpected: scale accepted array('f').")
    except Exception as e:
        print("\nAs expected, scale rejected array('f'):")
        print(" ", type(e).__name__ + ":", e)
Build key: a5d91a9aca8a988403003d9feb13944b8dbca7eb8f0ac8a20b86cef8a1ac9a94
Artifact : /home/circleci/.cache/scikitplot/cython/a5d91a9aca8a988403003d9feb13944b8dbca7eb8f0ac8a20b86cef8a1ac9a94/memview_scale_demo.cpython-311-x86_64-linux-gnu.so

Input type: array
Typecode   : d
Length     : 3
After Cython scale: [10.0, 20.0, 30.0]
Expected         : [10.0, 20.0, 30.0]

After Python scale: [10.0, 20.0, 30.0]

As expected, scale rejected array('f'):
  ValueError: Buffer dtype mismatch, expected 'double' but got 'float'

Tags: domain: cython plot-type: cython purpose: showcase

Total running time of the script: (0 minutes 1.853 seconds)

Related examples

Multi-file builds: .pxi includes and external headers

Multi-file builds: .pxi includes and external headers

C++ mode basics: cppclass and libcpp containers

C++ mode basics: cppclass and libcpp containers

Cython quickstart: compile_and_load

Cython quickstart: compile_and_load

Cache and restart reuse

Cache and restart reuse

Gallery generated by Sphinx-Gallery