BlogsDope image BlogsDope

memoryview() in Python

March 27, 2021 PYTHON FUNCTION 8381

I think hardly anyone of us has come across the memoryview() function in Python which is used for very specific applications and for performance improvement. The documentation of memoryview() says: memoryview objects allow Python to access the internal data of an object that supports the buffer protocol without copying. But what is this buffer protocol?

Buffer Protocol

It is a protocol in Python that allows to talk directly to a data structure and get its data out. This means that buffer protocol provides a way to access internal data of an object. Normally, we can access string data or integer data but what if we come through some huge piece of unusual data? Here, this protocol allows the object to share its data. This protocol is implemented at C level. It is only accessible to us at C-API level. Working with the buffer is faster than working through an object at high level Python. Reading the earlier stated definition from the documentation, we came across 'without copying'. Here, without copying means we are going to get the reference of the data rather than actually grabbing the data itself. 

So the buffer protocol allows us to access the data in a memoryview data object at Python level without any C language programming. This means that the object on which we are going to use the memoryview should support the buffer protocol. It turns out that there are only two objects in Python that can help us with this. They are bytes and bytearray. This was a very layman explanation of how buffer protocol and memoryview work. Let's explore about it below:

Definition of memoryview()

The Python memoryview() function returns a memoryview object of the given argument. Memoryview objects allow Python code to access internal buffers of an object by creating a memory view object.

When it comes to performance evaluation, it is more efficient to use memoryview object to create a number of slices on large byte strings or else we will end up with the copies of same data repeatedly. Let's see the syntax:

Syntax of memoryview()

memoryview(object)

As discussed earlier the parameter for memoryview() that is the object should support buffer protocol like bytes, bytearray, etc

Let's get straight to the examples:

Note: Learn about bytes() and bytearray() and then proceed to read ahead.

Examples of memoryview()

Let's create a bytes object first. The b prefix signifies a bytes string literal.

x = b'CodesDope'
print(type(x))

​<class 'bytes'>

Let's create a memoryview of this object x:

y = memoryview(x)
print(type(y))
print(y)

​<class 'memoryview'> 

<memory at 0x0000015AFEE1A280>

We can see that the memoryview object y prints memory at some address. Now what? What can we actually do with this? So, basically, now, we have the access to the values stored in x. Let's see what all all can we do with this:

x = b'CodesDope'
y = memoryview(x)
print(y.obj)
print(y.tolist())<br/>

​b'CodesDope' 

[67, 111, 100, 101, 115, 68, 111, 112, 101]

Here, y.obj prints the original data stored in x that is b'CodesDope'.

y.tolist() prints ASCII value of each letter in CodesDope like the ASCII value for C is 67, o is 111, d is 100 and so on. Let's use a bytearray this time:

x = bytearray("Python is Interesting","utf-8")
print(type(x))

y = memoryview(x)
print(y)
print(y[3])
print(chr(y[3]))

a = y.tobytes()
print(type(a))

<class 'bytearray'> 


<memory at 0x00000263825E7280> 

104 


b'Python is Interesting' 

<class 'bytes'>

See it carefully. x stores a bytearray type value. We created a memoryview of x and stored it in y. Now if we try indexing to get any value that is print(y[3]), we end up getting the ASCII value of it. So, we use the chr() function and get the value at a particular index. Also, in the end, y.tobytes() converts it from bytearray to bytes type. You can use different examples and try it yourself. Let's see one more example:

x = memoryview(b'42,56,45')
a = x[4]
print(a)
print(type(a))

54 

<class 'int'>

In the above code, ASCII value of 6 gets printed.

Let's see how slicing works here:

x = memoryview(b'Hello World')
a = x[2:10]
print(a)

​<memory at 0x00000292BEC77880>

We can see we didn't get the actual values. We just got the reference for it. So, to get the actual values we can use any of the two functions below:

x = memoryview(b'Hello World')
a = x[2:10]
print(a)

print(a.tobytes())
print(bytes(a))

print(a.tolist())
print(list(a))

​<memory at 0x00000292BEC77880> 

b'llo Worl' 

b'llo Worl' 

[108, 108, 111, 32, 87, 111, 114, 108]

[108, 108, 111, 32, 87, 111, 114, 108]

So as seen in the above code that we can use the bytes() or tobytes() function, we get the same result. Also, we can use the list() to tolist() to get the ASCII values of the same.

One last and important thing. What if we try to modify the memoryview object? Will it modify the original value as well? Let's see:

x = bytearray("CodesDope","utf-8")
y = memoryview(x)

y[5] = 90       # ASCII for letter Z
print(y.tobytes())
print(x)

​b'CodesZope' 

bytearray(b'CodesZope')

Yes, it got changed. The line y[5] = 90 changed the letter D at index 5 to Z as 90 is the ASCII value for Z. Also, after modifying it, if we try to print the original value of at the last line print(x), we get the modified value simply because we are modifying the memoryview object that is just the reference for  x stating that both refer to the same memory/buffer.


Liked the post?
Rarely seen, always noticed.
Editor's Picks
1 COMMENT

Please login to view or add comment(s).