One reason memoryviews are useful is that they can be sliced without copying the underlying data, unlike bytes/str.
For example, take the following toy example.
import time
for n in (100000, 200000, 300000, 400000):
data = b'x'*n
start = time.time()
b = data
while b:
b = b[1:]
print(f' bytes {n} {time.time() - start:0.3f}')
for n in (100000, 200000, 300000, 400000):
data = b'x'*n
start = time.time()
b = memoryview(data)
while b:
b = b[1:]
print(f'memoryview {n} {time.time() - start:0.3f}')
On my computer, I get
bytes 100000 0.211
bytes 200000 0.826
bytes 300000 1.953
bytes 400000 3.514
memoryview 100000 0.021
memoryview 200000 0.052
memoryview 300000 0.043
memoryview 400000 0.077
You can clearly see the quadratic complexity of the repeated string slicing. Even with only 400000 iterations, it's already unmanageable. Meanwhile, the memoryview version has linear complexity and is lightning fast.
Edit: Note that this was done in CPython. There was a bug in Pypy up to 4.0.1 that caused memoryviews to have quadratic performance.
Answer from Antimony on Stack OverflowOne reason memoryviews are useful is that they can be sliced without copying the underlying data, unlike bytes/str.
For example, take the following toy example.
import time
for n in (100000, 200000, 300000, 400000):
data = b'x'*n
start = time.time()
b = data
while b:
b = b[1:]
print(f' bytes {n} {time.time() - start:0.3f}')
for n in (100000, 200000, 300000, 400000):
data = b'x'*n
start = time.time()
b = memoryview(data)
while b:
b = b[1:]
print(f'memoryview {n} {time.time() - start:0.3f}')
On my computer, I get
bytes 100000 0.211
bytes 200000 0.826
bytes 300000 1.953
bytes 400000 3.514
memoryview 100000 0.021
memoryview 200000 0.052
memoryview 300000 0.043
memoryview 400000 0.077
You can clearly see the quadratic complexity of the repeated string slicing. Even with only 400000 iterations, it's already unmanageable. Meanwhile, the memoryview version has linear complexity and is lightning fast.
Edit: Note that this was done in CPython. There was a bug in Pypy up to 4.0.1 that caused memoryviews to have quadratic performance.
memoryview objects are great when you need subsets of binary data that only need to support indexing. Instead of having to take slices (and create new, potentially large, objects) to pass to another API, you can just take a memoryview object.
One such API example would be the struct module. Instead of passing in a slice of the large bytes object to parse out packed C values, you pass in a memoryview of just the region you need to extract values from.
memoryview objects, in fact, support struct unpacking natively; you can target a region of the underlying bytes object with a memoryview slice, then use .cast() to 'interpret' the underlying bytes as long integers, or floating point values, or n-dimensional lists of integers. This makes for very efficient binary file format interpretations, without having to create more copies of the bytes.