memoryview.toreadonly()
Zero-copy defence. Share a large buffer with code you do not fully trust, without copying it and without letting that code write to it.
Common call
safe = view.toreadonly()
Returns
memoryview — same bytes, writes rejected
Replaces
copying the buffer purely to stop someone modifying it
Watch out
it protects the VIEW, not the buffer — other writers still can
memoryview.toreadonly()
→ memoryview
Examples
1. Writes are rejected
ro = memoryview(bytearray(b'ab')).toreadonly()
ro[0] = 99
Returns
TypeError: cannot modify read-only memory2. Reading still works
ro.tolist()
Returns
[97, 98]3. The flag
ro.readonly
Returns
True4. Original unaffected
m = memoryview(bytearray(b'ab'))
m.toreadonly()
m.readonly
Returns
False5. Already read-only
memoryview(b'ab').toreadonly().readonly
Returns
True6. No copying
ro.nbytes
Returns
2 # same memory, not a duplicatePitfalls
1. It protects the view, not the buffer
The underlying bytearray is still mutable through any other reference. A read-only view stops the HOLDER of that view from writing; it does not freeze the data.
Data still changes
b = bytearray(b'ab') ro = memoryview(b).toreadonly() b[0] = 99 ro.tolist()
[99, 98] # changed underneath
Copy to freeze
frozen = bytes(memoryview(b))
an independent snapshot
2. The original view keeps write access
toreadonly returns a NEW view; it does not downgrade the one you called it on. Forgetting to use the returned object leaves the writable view in play.
Return value ignored
m.toreadonly() m[0] = 99
succeeds — m was never restricted
Use what it returns
ro = m.toreadonly() ro[0] = 99
TypeError
3. There is no way back
No toWritable exists. Once you hold only the read-only view, regaining write access means going back to the original buffer or view — so do not discard those if you will need to write later.
No reverse method
ro.towritable()
AttributeError: 'memoryview' object has no attribute 'towritable'
Keep the original
m = memoryview(b) ro = m.toreadonly() # keep m
m still writes
4. Python 3.8 and newer only
On older versions the method does not exist. The pre-3.8 way to hand out a non-writable view was to copy into bytes first, which costs the memory a view was meant to save.
Fails on 3.7
view.toreadonly()
AttributeError: 'memoryview' object has no attribute 'toreadonly'
Copy instead
memoryview(bytes(view))
read-only, but copies
When to use
Use it
- Passing a large buffer to code that should only read it
- Public APIs exposing internal buffers without copying
- Enforcing an intent that a comment alone would not
Reach for something else
- You need a genuine immutable snapshot → copy to bytes
- The buffer is already immutable bytes — it is already read-only
- Supporting Python 3.7 or older without a fallback
Notes
Complexity
O(1) — new view metadata, no data touched
Return
A new memoryview sharing the original memory
CPython impl
Objects/memoryobject.c :: memory_toreadonly
Memory
No copying; the new view keeps the buffer alive and exported
Thread-safe
Reading is safe; the source can still change under you via other references
FAQ
No — only that view. Anyone holding the original bytearray or a writable view can still change the bytes, and your read-only view will see the change. For a genuine snapshot you have to copy.
frozen = bytes(view) # independent
History
3.8
memoryview.toreadonly added, giving a zero-copy way to share buffers safely.