memoryview.tobytes()

The deliberate exit from zero-copy. A memoryview exists to avoid copying; tobytes is you choosing to pay for one.

Memoryview methodPython 2.7+Live demo
Common call
data = view.tobytes()
Returns
bytes — an independent copy
Replaces
bytes(view), which does the same thing
Watch out
it copies — calling it in a loop throws away the reason you used a view
memoryview.tobytes(orderorder — Element order for multi-dimensional views: 'C', 'F' or 'A'. Irrelevant for the one-dimensional byte views most code uses.type: str · default: None=None)
→ bytes

Demo

Live evaluation
Try:
Inputs
sstrtext to view as bytes
Output
memoryview(bytes('abc', 'utf-8')).tobytes()
b'abc'

tobytes hands back the bytes the view is looking at, as an independent copy. The non-ascii case is the instructive one: the text is encoded as UTF-8 first, so the accented character occupies two bytes and shows as an escape in the result. A memoryview never reinterprets those bytes as text — it only ever exposes raw memory.

Parameters

NameTypeRequiredDescription
orderstrno (None)Element order for multi-dimensional views: 'C', 'F' or 'A'. Irrelevant for the one-dimensional byte views most code uses.

Return value

bytes — A new bytes object holding a COPY of the data the view exposes. The original buffer is unchanged.

Common patterns

Slice without copying, then copy once
The whole point — the slicing is free, and you pay only for the part you keep.
view = memoryview(big_buffer)
header = view[:16].tobytes()
Hand bytes to an API that will not take a view
Some libraries insist on real bytes; this is the conversion point.
legacy_api(view.tobytes())
Detach from a buffer you are about to mutate
A copy stays valid after the underlying array changes.
snapshot = memoryview(buf).tobytes()

Examples

1. Plain ascii
memoryview(b'abc').tobytes()
Returns
b'abc'
2. A slice
memoryview(b'hello')[1:3].tobytes()
Returns
b'el'
3. Empty
memoryview(b'').tobytes()
Returns
b''
4. Same as bytes()
bytes(memoryview(b'abc'))
Returns
b'abc'
5. It is a copy
v = memoryview(bytearray(b'ab')) d = v.tobytes() v[0] = 99 d
Returns
b'ab' # unchanged
6. Non-ascii
memoryview('é'.encode()).tobytes()
Returns
b'\xc3\xa9'

Pitfalls

1. It copies — that is the cost you were avoiding
memoryview exists so large buffers can be sliced without duplication. Calling tobytes inside a loop reintroduces exactly the copying the view was there to prevent.
Copies every chunk
for i in range(0, len(v), 1024):
    process(v[i:i+1024].tobytes())
one allocation per chunk
Pass the view
for i in range(0, len(v), 1024):
    process(v[i:i+1024])
no copying
2. The copy stops tracking the buffer
A view reflects later changes to the underlying data; the bytes it produces do not. That is usually what you want, but it surprises anyone expecting a live window.
Frozen snapshot
buf = bytearray(b'ab')
d = memoryview(buf).tobytes()
buf[0] = 99
d
b'ab' # stale
Keep the view
v = memoryview(buf)
buf[0] = 99
v.tobytes()
b'cb' # current
3. Bytes are not text
tobytes gives raw bytes, never a str. Non-ASCII characters appear as multi-byte escapes, and turning them back into text needs an explicit decode with the right encoding.
Not a string
memoryview('é'.encode()).tobytes()
b'\xc3\xa9'
Decode it
memoryview('é'.encode()).tobytes().decode('utf-8')
'é'

When to use

Use it
  • Extracting a small piece of a large buffer, once
  • Passing data to an API that will not accept a buffer protocol object
  • Snapshotting bytes before the underlying buffer changes
Reach for something else
  • Inside a hot loop over chunks — pass the view itself
  • You only need to read a few bytes → index the view directly
  • You want text → decode after converting, with an explicit encoding

Notes

Complexity
O(n) — a full copy of the exposed region
Return
A new bytes object, independent of the source buffer
CPython impl
Objects/memoryobject.c :: memory_tobytes
Memory
Allocates a buffer the size of the view
Thread-safe
The copy is atomic enough, but a concurrently mutated source gives a torn result

FAQ

No — they produce the same copy. tobytes is more explicit about what is happening, and it accepts the order argument for multi-dimensional views, which the bytes constructor does not.

bytes(memoryview(b'abc')) == memoryview(b'abc').tobytes()
# True

History

2.7
memoryview introduced with tobytes, replacing the older buffer type.
3.8
The order argument added for multi-dimensional views.