Memoryview: примеры (PYTHON)

Функция memoryview в Python: практическое руководство с примерами
Раздел: Встроенные типы, Байтовые операции
memoryview(object: bytes or bytearray): memoryview

Функция memoryview в Python

Функция memoryview() возвращает объект memory view, который предоставляет буферный протокол для доступа к внутренним данным объекта без копирования. Этот объект позволяет просматривать память, используемую другими объектами, поддерживающими буферный протокол, такие как bytes, bytearray и массивы из модуля array.

Когда используется memoryview

Использование memoryview эффективно при работе с большими объемами данных, когда необходимо избежать накладных расходов на копирование. Типичные сценарии включают обработку двоичных данных, работу с сетевыми пакетами, манипуляции с аудио- или видеобуферами, а также операции с массивами в научных вычислениях.

Аргументы функции

Функция принимает один обязательный аргумент:

  • obj - объект, поддерживающий буферный протокол. Это может быть bytes, bytearray, массив из модуля array или другие объекты, реализующие методы __buffer__().

Возвращаемое значение

Функция возвращает объект memoryview, который является представлением памяти исходного объекта. Этот объект поддерживает индексацию, срезы и может быть преобразован в другие типы, такие как bytes или bytearray. Важной особенностью является то, что изменения в memoryview отражаются на исходном объекте, если он изменяем.

Короткие примеры использования memoryview

Базовое создание memoryview

Создание memoryview из bytes и bytearray:

data = b'Hello, World!'
mv = memoryview(data)
print(mv)
<memory at 0x7f8e3c1b5a40>
arr = bytearray(b'Python')
mv = memoryview(arr)
print(mv[0])
print(bytes(mv[1:3]))
80
b'yt'

Изменение данных через memoryview

Изменение bytearray через memoryview:

arr = bytearray(b'abcdef')
mv = memoryview(arr)
mv[2] = 90  # ASCII код 'Z'
print(arr)
bytearray(b'abZdef')

Использование срезов

Срезы в memoryview возвращают новый memoryview:

data = b'0123456789'
mv = memoryview(data)
slice_mv = mv[3:7]
print(slice_mv.tobytes())
print(mv.release())  # Освобождение ресурсов
b'3456'
None

Похожие функции в Python

В Python существуют другие способы работы с буферами памяти:

  • slice - стандартная операция среза для последовательностей, которая создает копию данных. В отличие от memoryview, срезы bytes или bytearray копируют данные, что может быть неэффективно для больших объектов.
  • struct.unpack - функция из модуля struct для преобразования двоичных данных в кортеж Python. Полезно для чтения структурированных данных, но требует копирования.
  • io.BytesIO - класс для работы с потоками байтов в памяти. Предоставляет интерфейс, подобный файлу, но также копирует данные при операциях.
  • numpy.ndarray - массив из библиотеки NumPy, который обеспечивает продвинутые операции с памятью и часто используется в научных вычислениях. Memoryview может взаимодействовать с массивами NumPy через буферный протокол.

Использование memoryview предпочтительно при необходимости избежать копирования больших объемов данных и при работе с изменяемыми буферами, такими как bytearray.

Альтернативы в других языках программирования

JavaScript: ArrayBuffer и DataView

В JavaScript для работы с бинарными данными используются ArrayBuffer и DataView. ArrayBuffer представляет буфер памяти, а DataView предоставляет интерфейс для чтения и записи данных.

let buffer = new ArrayBuffer(16);
let view = new DataView(buffer);
view.setUint8(0, 65);
console.log(view.getUint8(0));
65

Java: ByteBuffer

В Java класс ByteBuffer из пакета java.nio позволяет создавать буферы для примитивных типов данных.

import java.nio.ByteBuffer;
ByteBuffer buf = ByteBuffer.allocate(8);
buf.put((byte) 42);
buf.flip();
System.out.println(buf.get());
42

C#: Memory<T> и Span<T>

В C# типы Memory<T> и Span<T> предоставляют безопасные представления памяти для работы с массивами и другими структурами.

byte[] array = new byte[10];
Memory<byte> memory = array.AsMemory();
Span<byte> span = memory.Span;
span[0] = 100;
Console.WriteLine(array[0]);
100

Golang: срезы байтов

В Go срезы байтов ([]byte) являются представлением массива и не копируют данные при создании.

package main
import "fmt"
func main() {
    array := [3]byte{1, 2, 3}
    slice := array[:]
    slice[0] = 100
    fmt.Println(array[0])
}
100

Отличия от Python

В отличие от Python, в некоторых языках, таких как Java и C#, работа с буферами памяти требует более явного управления типами и памятью. В JavaScript и Go срезы часто используются более интуитивно, но с различной семантикой безопасности.

Типичные ошибки при использовании memoryview

Попытка изменить неизменяемый объект

При попытке изменить memoryview, созданный из неизменяемого объекта bytes, возникает ошибка TypeError.

data = b'Hello'
mv = memoryview(data)
mv[0] = 65
TypeError: cannot modify read-only memory

Использование памяти после release()

После вызова метода release() доступ к memoryview вызывает ValueError.

mv = memoryview(b'test')
mv.release()
print(mv[0])
ValueError: operation forbidden on released memoryview object

Несоответствие форматов данных

При работе с memoryview для сложных структур данных возможны ошибки, если формат не соответствует ожиданиям.

import array
arr = array.array('i', [1, 2, 3])
mv = memoryview(arr)
print(mv.cast('b')[0])  # Попытка интерпретировать как байты
1

Изменения в последних версиях Python

В Python 3.8 была добавлена поддержка многомерных memoryview для массива чисел. Также улучшена производительность операций с memoryview.

В Python 3.11 оптимизирована работа memoryview с буферным протоколом, что ускорило выполнение операций чтения и записи.

Начиная с Python 3.12, memoryview поддерживает новые форматы данных для работы с расширенными типами массивов, включая структуры с упаковкой.

Расширенные примеры использования memoryview

Работа с многомерными массивами

Использование memoryview для представления многомерных данных из модуля array.

Пример python
import array
arr = array.array('i', [1, 2, 3, 4, 5, 6])
mv = memoryview(arr)
# Создание двумерного представления
mv_2d = mv.cast('i', [2, 3])
print(mv_2d[1][2])
6

Эффективное копирование данных

Копирование данных между буферами без промежуточных копий.

Пример python
src = bytearray(b'123456789')
dst = bytearray(9)
src_mv = memoryview(src)
dst_mv = memoryview(dst)
dst_mv[:] = src_mv[:]
print(dst)
bytearray(b'123456789')

Обработка двоичных структур

Использование memoryview для чтения структурированных двоичных данных.

Пример python
import struct
data = bytearray(struct.pack('2i f', 10, 20, 3.14))
mv = memoryview(data)
a, b, c = struct.unpack('2i f', mv)
print(a, b, c)
10 20 3.140000104904175

Взаимодействие с NumPy

Memoryview может использоваться для обмена данными с массивами NumPy без копирования.

Пример python
import numpy as np
arr_np = np.array([1, 2, 3], dtype=np.int32)
mv = memoryview(arr_np)
print(mv.tobytes())
arr_np[0] = 100
print(mv.tobytes())
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'
b'd\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'

Создание кольцевого буфера

Реализация кольцевого буфера с использованием memoryview для эффективного доступа к данным.

Пример python
class CircularBuffer:
    def __init__(self, size):
        self.buffer = bytearray(size)
        self.view = memoryview(self.buffer)
    def write(self, data, offset):
        offset %= len(self.buffer)
        self.view[offset:offset+len(data)] = data
buf = CircularBuffer(10)
buf.write(b'ABCD', 8)
print(buf.view.tobytes())
b'AB\x00\x00\x00\x00\x00\x00\x00CD'

питон memoryview function comments

En
Memoryview Return a memory view object