Skip to main content

Objects/memoryobject.c

Source:

cpython 3.14 @ ab2d84fe1023/Objects/memoryobject.c

memoryview exposes the buffer protocol as a Python object. It enables zero-copy slicing of bytes, bytearray, and any object implementing bf_getbuffer.

Map

LinesSymbolRole
1-150memoryview_newAcquire buffer, initialize view metadata
151-350memory_getitem, memory_setitemIndex/slice with strides support
351-600memory_subscriptMulti-dimensional indexing mv[i, j]
601-800memory_castmv.cast('B') — reinterpret format
801-1000memory_tobytesCopy to bytes (respects strides/shape)
1001-1200memory_tolistConvert to nested Python lists
1201-1400memory_releaseRelease buffer export, decrement ob_exports
1401-1600Buffer protocolmemoryview itself exposes a buffer
1601-2000ComparisonsElement-by-element compare
2001-3200Contiguity checksC-contiguous, Fortran-contiguous, suboffset handling

Reading

Buffer acquisition

// CPython: Objects/memoryobject.c:88 memoryview_new
static PyObject *
memoryview_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
{
PyObject *master;
PyArg_ParseTuple(args, "O:memoryview", &master);
PyMemoryViewObject *mview = (PyMemoryViewObject *)
PyObject_GC_New(PyMemoryViewObject, subtype);
/* Acquire the buffer from master */
if (PyObject_GetBuffer(master, &mview->view, PyBUF_FULL_RO) < 0) {
...
}
mview->master.obj = Py_NewRef(master);
return (PyObject *)mview;
}

memoryview(b'hello') acquires a read-only buffer. memoryview(bytearray(...)) is writable.

Slicing with strides

// CPython: Objects/memoryobject.c:220 memory_getslice
static PyObject *
memory_getslice(PyMemoryViewObject *self, Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step)
{
/* Compute new view: same buffer, adjusted buf + offset, new shape/strides */
PyMemoryViewObject *sliced = (PyMemoryViewObject *)
PyObject_GC_New(PyMemoryViewObject, &PyMemoryView_Type);
sliced->view = self->view; /* shallow copy */
sliced->view.buf = (char *)self->view.buf + start * self->view.strides[0];
sliced->view.shape[0] = (stop - start + step - 1) / step;
sliced->view.strides[0] = self->view.strides[0] * step;
return (PyObject *)sliced;
}

No data is copied. The slice has a new buf pointer and strides.

cast

// CPython: Objects/memoryobject.c:650 memory_cast
static PyObject *
memory_cast(PyMemoryViewObject *self, PyObject *args, PyObject *kwds)
{
/* Reinterpret the bytes using a new format string */
/* e.g. mv.cast('I') on a bytes memoryview → view of unsigned ints */
...
if (ndim == 1) {
new_itemsize = get_itemsize_from_format(format);
newview->view.len = self->view.len;
newview->view.itemsize = new_itemsize;
newview->view.shape[0] = self->view.len / new_itemsize;
}
...
}

memoryview(b'\x01\x00\x02\x00').cast('H') gives a view of two uint16 values [1, 2].

tobytes

// CPython: Objects/memoryobject.c:840 memory_tobytes
static PyObject *
memory_tobytes(PyMemoryViewObject *self, PyObject *args)
{
/* For C-contiguous views: single memcpy */
if (PyBuffer_IsContiguous(&self->view, 'C')) {
return PyBytes_FromStringAndSize(self->view.buf, self->view.len);
}
/* Non-contiguous: gather each element via strides */
...
}

release

// CPython: Objects/memoryobject.c:1240 memory_release
static PyObject *
memory_release(PyMemoryViewObject *self, PyObject *Py_UNUSED(ignored))
{
PyBuffer_Release(&self->view); /* decrements ob_exports on the master */
self->master.obj = NULL; /* no longer referencing master */
Py_RETURN_NONE;
}

Releasing a memoryview allows the master object (e.g., bytearray) to be resized again.

gopy notes

memoryview is in objects/memoryview.go. The view holds a pointer into the original object's backing []byte. Slicing adjusts the pointer and length without copying. cast changes the itemsize and recomputes shape. tobytes uses copy for contiguous views; non-contiguous views iterate with stride arithmetic. release decrements the master's export count.