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] = 65TypeError: 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.
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
Эффективное копирование данных
Копирование данных между буферами без промежуточных копий.
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 для чтения структурированных двоичных данных.
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 без копирования.
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 для эффективного доступа к данным.
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'