Новое в Python 3.14 Тип
memoryviewтеперь поддерживает подписку (subscription), что делает его обобщённым (дженерик) типом.
Объекты memoryview позволяют коду Python получать доступ к внутренним данным объекта, который поддерживает буферный протокол, без копирования.
В Python объекты memoryview обрабатываются встроенным классом memoryview(). Встроенный класс memoryview() имеет несколько своих методов.
Memoryview имеет понятие элемента, который является атомарной единицей памяти, обрабатываемой исходным объектом obj. Для многих простых типов, таких как bytes и bytearray, элемент является одним байтом, но другие типы, такие как array.array может иметь более крупные элементы.
Значение len(view) соответствует длине метода memoryview.tolist(), который возвращает вложенный список, представляющий данные из memoryview. Если view.ndim == 1, то это значение равно количеству элементов в представлении.
Изменено в Python 3.12: Теперь при
view.ndim == 0вызовlen(view)вызывает исключениеTypeError, вместо того чтобы возвращать число 1.
До Python 3.12:
>>> m = memoryview(array.array('i', [])) >>> m.ndim = 0 >>> len(m) 1
Это было нелогично, потому что объект с нулевой размерностью (
ndim == 0) не должен иметь длину больше 0.
Начиная с Python 3.12 такая конструкция вызывает ошибку, потому что объекты с
ndim == 0не имеют определённой длины.
>>> m = memoryview(array.array('i', [])) >>> m.ndim = 0 >>> len(m) # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # TypeError: len() of unsized object
Атрибут memoryview.itemsize даст количество байт в одном элементе.
Memoryview поддерживает срезы и индексирование для предоставления своих данных. Одномерное нарезание приведет к подпредставлению:
>>> v = memoryview(b'abcefg') >>> v[1] # 98 >>> v[-1] # 103 >>> v[1:4] # <memory at 0x7f3ddc9f4350> >>> bytes(v[1:4]) # b'bce'
Если memoryview.format является одним из собственных спецификаторов формата из модуля struct, индексация с целым числом или кортежем целых чисел также поддерживается и возвращает один элемент с правильным типом. Одномерные memoryview можно индексировать целочисленным или одноцелым кортежем. Многомерные memoryview можно индексировать с помощью кортежей точно ndim целых чисел, где ndim - это число измерений. Представления нулевой размерности памяти можно индексировать с помощью пустого кортежа.
Вот пример с небайтовым форматом:
>>> import array >>> a = array.array('l', [-11111111, 22222222, -33333333, 44444444]) >>> m = memoryview(a) >>> m[0] # -11111111 >>> m[-1] # 44444444 >>> m[::2].tolist() # [-11111111, -33333333]
Если базовый объект доступен для записи, то memoryview поддерживает одномерное назначение среза. Изменение размера не допускается:
>>> data = bytearray(b'abcefg') >>> v = memoryview(data) >>> v.readonly # False >>> v[0] = ord(b'z') >>> data # bytearray(b'zbcefg') >>> v[1:4] = b'123' >>> data # bytearray(b'z123fg') >>> v[2:3] = b'spam' # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # ValueError: memoryview assignment: lvalue and rvalue have different structures >>> v[2:6] = b'spam' >>> data # bytearray(b'z1spam')
Одномерное memoryviews хешируемых (только для чтения) типов с форматами 'B', 'b' или 'c' также можно хешировать. Хеш определяется как: hash(m) == hash(m.tobytes()):
>>> v = memoryview(b'abcefg') >>> hash(v) == hash(b'abcefg') # True >>> hash(v[2:4]) == hash(b'ce') # True >>> hash(v[::-2]) == hash(b'abcefg'[::-2]) # True
Тип
memoryviewтеперь поддерживает подписку (subscription), что делает его обобщённым (дженерик) типом.
"Subscription" в контексте типов означает, что можно указать тип элементов, которые содержит объект, используя нотацию вроде:
from typing import TypeVar T = TypeVar('T') class MyContainer(list[T]): ...
В случае memoryview, теперь можно сделать так:
from typing import Any # Это говорит, что memoryview содержит целые числа x: memoryview[int]
Это не влияет на выполнение кода, но помогает статическим анализаторам типов (например, mypy, pyright) лучше понимать, с какими данными ты работаешь.
Обобщённый тип (или generic type) - это тип, который может принимать параметры типа, чтобы уточнить, какие данные он содержит.
Например:
list[int] # список целых чисел dict[str, int] # словарь из строк и целых чисел set[float] # множество чисел с плавающей точкой
Теперь такая возможность есть и у memoryview.
from typing import Any def process(data: memoryview[int]) -> None: for byte in data: print(byte) data = memoryview(bytearray(b'Hello')) process(data) # Теперь тип проверяется явно
Обрати внимание: эта проверка работает только на уровне типизации. В рантайме
memoryviewвсё равно не знает, какой тип вы указали через[].
mypy.memoryview более согласованным с другими коллекциями, поддерживающими дженерики.