What does memoryview.cast require of a view, and when does it raise?
answer
- Same bytes, different element description
- The layout must have no gaps
- Byte length has to divide evenly
- len() counts items, nbytes counts bytes
- Cast formats are native order only
basics
~20 smemoryview.cast reinterprets the same bytes under a different element format or shape without copying. It requires a C-contiguous view and a total byte length that divides evenly by the new item size; otherwise it raises TypeError.
solid answer
~40 s`memoryview.cast(format)` returns a new view over the *same* memory reinterpreted with a different element type — `cast("I")` reads four-byte unsigned integers out of a byte buffer, with no copy and no `struct` round trip. Two conditions must hold. The source view must be C-contiguous: casting a strided view such as `memoryview(buf)[::2]` raises `TypeError: memoryview: casts are restricted to C-contiguous views`, because the elements are not laid out back to back. And `memoryview.nbytes` must be an exact multiple of the new `itemsize`, or you get `TypeError: memoryview: length is not a multiple of itemsize`. The optional `shape` argument reshapes at the same time, so a flat 6-byte view can become a 2×3 view. After a cast, `len()` is the item count while `memoryview.nbytes` is unchanged — that gap is the usual source of confusion.
code
python · 5 linesbuf = bytearray(8)
view = memoryview(buf)
words = view.cast("I")
words[0] = 7
print(view.format, words.format, words.itemsize, len(words), bytes(buf))go deeper
Recall that memoryview.cast reinterprets the same memory with a different element type rather than converting or copying it, and that cast("I") over bytes gives you four-byte integers.
Explain the two preconditions — a C-contiguous source and a byte length that divides by the new item size — and the difference between len() as an item count and memoryview.nbytes as a byte count.
Show the judgement about when a cast is the wrong tool: heterogeneous records or a specified wire byte order belong to struct.unpack_from over the same view, since a cast uses native order and fails silently rather than raising.
Own the platform assumption. Native-order reinterpretation ties a data path to the machine that wrote it, so decide where in the system layout is declared explicitly versus assumed, and make sure that choice is documented rather than embedded in a fast path nobody re-reads.
## What a cast is The buffer protocol describes a block of memory with a format code, an item size, a shape and strides. `memoryview.cast(format)` keeps the block and rewrites the description: same pointer, same `nbytes`, different element type. `cast("I")` over an 8-byte buffer yields a view of two native unsigned integers; writing `words[0] = 7` writes four bytes into the underlying `bytearray`. Nothing is copied and no `struct.pack` round trip happens. This is the zero-copy alternative to `struct.unpack` for homogeneous data. Where `struct` reads bytes and builds Python objects, `cast` changes how the same bytes are addressed, and elements are materialized as Python ints only when you index one. ## The two restrictions **C-contiguity.** A cast is only meaningful when the elements sit back to back with no gaps, because reinterpreting the item width is arithmetic on a flat run of bytes. A view with a non-unit stride — `memoryview(bytearray(8))[::2]`, whose `strides` is `(2,)` and whose `c_contiguous` is `False` — cannot be cast, and raises `TypeError: memoryview: casts are restricted to C-contiguous views`. The properties to check first are `memoryview.c_contiguous`, `memoryview.f_contiguous` and `memoryview.contiguous`. **Exact division.** The total byte length must be a whole number of new items. `memoryview(bytearray(7)).cast("I")` raises `TypeError: memoryview: length is not a multiple of itemsize`, because seven bytes is not a whole number of four-byte integers. This is the check that catches a truncated record before it becomes silent garbage, so it is worth letting it fire rather than padding around it. ## Shape, strides and the count that changes `cast` takes an optional `shape`, so one call can both reinterpret and reshape: `memoryview(bytearray(6)).cast("B", shape=(2, 3))` produces a two-dimensional view with `shape` `(2, 3)`, `strides` `(3, 1)` and `ndim` `2`. Indexing it takes a tuple. Flattening back is another `cast` to a one-dimensional format. The trap after any cast is the difference between the two length notions: `memoryview.nbytes` is always the byte count and never changes across a cast, while `len()` and `memoryview.shape` report *items*, so an 8-byte view has `len()` of 8 as `"B"` and 2 as `"I"`. Code that mixes the two — passing `len(view)` where a byte count is wanted — reads or writes a quarter of what it intended. ## Format codes and endianness Cast formats are restricted to native single-element codes: `"B"`, `"b"`, `"h"`, `"H"`, `"i"`, `"I"`, `"l"`, `"L"`, `"q"`, `"Q"`, `"f"`, `"d"`, and so on. That means casts use *native* byte order and native alignment — there is no little-endian or big-endian prefix as there is in `struct`. Reading a wire format with a fixed endianness through `cast` is therefore only correct on a machine whose native order matches; for data crossing machines, `struct.unpack_from` with an explicit `<` or `>` prefix is the right tool, and it also accepts the view directly, so you still avoid the copy. Getting this wrong produces plausible-looking numbers rather than an error, which is why it is worth stating explicitly in any code that casts. ## Read-only and lifetime A cast inherits the read-only flag from the source view, so casting a view of `bytes` gives a read-only integer view — reinterpretation never grants write access that the exporter withheld. `memoryview.toreadonly()` goes the other way, narrowing a writable view before you pass it on. Each cast is a distinct view with its own export, so the exporter stays pinned until every one of them is released, and each should be released or scoped in a `with` block. ## When to reach for it `cast` earns its place when you have a homogeneous block of fixed-width values in native order — samples, counters, fixed-width records — and want to index or mutate them without building intermediate objects. For heterogeneous records, mixed field widths or a specified wire byte order, `struct.unpack_from` over the same view is clearer and correct on every platform. The decision is not about speed alone: a cast that is wrong about endianness or alignment fails silently, whereas `struct` states the layout in one string a reviewer can read.
- After casting a view to "I", why do len() and nbytes disagree?`memoryview.nbytes` is always the size of the block in bytes and a cast never changes it. `len()` and `memoryview.shape` report items, so an 8-byte view has `len()` 8 as `"B"` and 2 as `"I"`, with `memoryview.itemsize` 4. Passing `len(view)` where a byte count is expected then addresses a quarter of the buffer, which is the usual post-cast bug.
- Can you use cast to read a big-endian wire format?Not safely. Cast formats are native single-element codes, so the byte order is whatever the machine uses; there is no `<` or `>` prefix as in `struct`. On a little-endian host a big-endian field read through a cast produces a plausible wrong number with no error. Use `struct.unpack_from` with an explicit prefix — it takes the view directly, so the read is still copy-free.
- Does casting a view of a bytes object give you a writable view?No. The read-only flag comes from the exporter and propagates through every derived view, so a cast of a view over `bytes` is still read-only and assignment raises `TypeError`. Reinterpretation never grants access the exporter withheld. The only direction available is narrowing, with `memoryview.toreadonly()`.
saying these in an interview costs you the question
- Thinks cast copies or converts the underlying bytes
- Casts a strided or sliced-with-step view
- Ignores that nbytes must divide by the new itemsize
- Uses len() as a byte count after casting
- Assumes cast formats honour a specified endianness
- Expects a cast to make a read-only view writable