Проверка документации по memoryview:
Объекты memoryview позволяют коду Python получать доступ к внутренним данным объекта, который поддерживает протокол буфера, без копирования.
класс memoryview (объект)
Создайте представление памяти, которое ссылается на obj. obj должен поддерживать протокол буфера. Встроенные объекты, поддерживающие протокол буфера, включают байты и массив байтов.
Затем нам дается пример кода:
>>> v = memoryview(b'abcefg')
>>> v[1]
98
>>> v[-1]
103
>>> v[1:4]
<memory at 0x7f3ddc9f4350>
>>> bytes(v[1:4])
b'bce'
Цитата окончена, теперь давайте рассмотрим подробнее:
>>> b = b'long bytes stream'
>>> b.startswith(b'long')
True
>>> v = memoryview(b)
>>> vsub = v[5:]
>>> vsub.startswith(b'bytes')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'memoryview' object has no attribute 'startswith'
>>> bytes(vsub).startswith(b'bytes')
True
>>>
Итак, что я понял из вышесказанного:
Мы создаем объект memoryview для предоставления внутренних данных объекта буфера без копирования, однако, чтобы сделать что-нибудь полезное с объектом (путем вызова методов, предоставленных объектом), мы должны создать копию!
Обычно memoryview (или старый буферный объект) потребуется, когда у нас есть большой объект, и срезы тоже могут быть большими. Потребность в большей эффективности будет присутствовать, если мы делаем большие ломтики или маленькие ломтики, но большое количество раз.
С приведенной выше схемой я не понимаю, как она может быть полезна в любой ситуации, если только кто-нибудь не объяснит мне, чего мне здесь не хватает.
Edit1:
У нас есть большой кусок данных, мы хотим обработать его, продвигаясь по нему от начала до конца, например, извлекая токены из начала строкового буфера до тех пор, пока буфер не будет использован. В термине C это продвигает указатель через buffer, а указатель может быть передан любой функции, ожидающей типа буфера. Как можно сделать что-то подобное в Python?
Люди предлагают обходные пути, например, многие строковые и регулярные функции принимают аргументы позиции, которые можно использовать для имитации продвижения указателя. С этим есть две проблемы: во-первых, это обходной путь, вы вынуждены изменить свой стиль кодирования, чтобы преодолеть недостатки, и, во-вторых, не все функции имеют аргументы позиции, например функции регулярных выражений и startswith
do, encode()
/ decode()
not.
Другие могут предложить загружать данные порциями или обрабатывать буфер небольшими сегментами, размер которых превышает максимальный токен. Итак, мы знаем об этих возможных обходных путях, но мы должны работать в Python более естественным образом, не пытаясь изменить стиль кодирования в соответствии с языком - не так ли?
Edit2:
Образец кода проясняет ситуацию. Это то, что я хочу сделать, и то, что я предполагал, с первого взгляда позволит мне сделать memoryview. Давайте использовать pmview (правильное представление памяти) для функциональности, которую я ищу:
tokens = []
xlarge_str = get_string()
xlarge_str_view = pmview(xlarge_str)
while True:
token = get_token(xlarge_str_view)
if token:
xlarge_str_view = xlarge_str_view.vslice(len(token))
# vslice: view slice: default stop paramter at end of buffer
tokens.append(token)
else:
break
источник
Ответы:
Одна из причин, по которой
memoryview
s полезны, заключается в том, что их можно разрезать без копирования базовых данных, в отличие отbytes
/str
.Например, возьмем следующий игрушечный пример.
import time for n in (100000, 200000, 300000, 400000): data = 'x'*n start = time.time() b = data while b: b = b[1:] print 'bytes', n, time.time()-start for n in (100000, 200000, 300000, 400000): data = 'x'*n start = time.time() b = memoryview(data) while b: b = b[1:] print 'memoryview', n, time.time()-start
На моем компьютере я получаю
bytes 100000 0.200068950653 bytes 200000 0.938908100128 bytes 300000 2.30898690224 bytes 400000 4.27718806267 memoryview 100000 0.0100269317627 memoryview 200000 0.0208270549774 memoryview 300000 0.0303030014038 memoryview 400000 0.0403470993042
Вы можете ясно видеть квадратичную сложность повторяющейся нарезки строки. Даже после 400000 итераций это уже невозможно. Между тем, версия memoryview имеет линейную сложность и молниеносно.
Изменить: обратите внимание, что это было сделано в CPython. В Pypy до 4.0.1 была ошибка, из-за которой просмотры памяти имели квадратичную производительность.
источник
TypeError: memoryview: a bytes-like object is required, not 'str'
print
как оператор также не работает в Python 3. Этот код был написан для Python 2, хотя изменения, необходимые для Python 3, довольно тривиальны.str
в python3 совершенно другое определение в python2.memoryview
Объекты отлично подходят, когда вам нужны подмножества двоичных данных, которые должны поддерживать только индексирование. Вместо того, чтобы делать срезы (и создавать новые, потенциально большие) объекты для передачи другому API, вы можете просто взятьmemoryview
объект.Одним из таких примеров API может быть
struct
модуль. Вместо того, чтобы передавать фрагмент большогоbytes
объекта для анализа упакованных значений C, вы передаетеmemoryview
только ту область, из которой необходимо извлечь значения.memoryview
объекты фактически поддерживаютstruct
распаковку изначально; вы можете нацелить область базовогоbytes
объекта с помощью среза, а затем использовать.cast()
для «интерпретации» базовых байтов как длинных целых чисел, значений с плавающей запятой или n-мерных списков целых чисел. Это обеспечивает очень эффективную интерпретацию формата двоичного файла без необходимости создания дополнительных копий байтов.источник
memoryview
. Тогда вы имеете дело с текстом, а не с двоичными данными.Позвольте мне объяснить, в чем заключается проблема понимания.
Спрашивающий, как и я, ожидал, что сможет создать представление памяти, которое выбирает часть существующего массива (например, bytes или bytearray). Поэтому мы ожидали чего-то вроде:
desired_slice_view = memoryview(existing_array, start_index, end_index)
Увы, такого конструктора нет, и вместо этого в документации не указывается, что делать.
Ключевым моментом является то, что вы должны сначала создать представление памяти, которое охватывает весь существующий массив. Из этого представления памяти вы можете создать второе представление памяти, которое покрывает часть существующего массива, например:
whole_view = memoryview(existing_array) desired_slice_view = whole_view[10:20]
Короче говоря, цель первой строки - просто предоставить объект, реализация среза которого (dunder-getitem) возвращает представление в памяти.
Это может показаться неряшливым, но можно рационализировать это несколькими способами:
Наш желаемый результат - это просмотр памяти, который представляет собой кусок чего-то. Обычно мы получаем срезанный объект из объекта того же типа, используя для него оператор среза [10:20]. Так что есть основания ожидать, что нам нужно получить желаемый_slice_view из memoryview, и, следовательно, первым шагом будет получение memoryview всего базового массива.
Наивное ожидание конструктора memoryview с аргументами start и end не учитывает, что спецификация среза действительно требует всей выразительности обычного оператора среза (включая такие вещи, как [3 :: 2] или [: -4] и т. Д.). Невозможно просто использовать существующий (и понятный) оператор в этом однострочном конструкторе. Вы не можете присоединить его к аргументу existing_array, так как это сделает часть этого массива, вместо того, чтобы сообщить конструктору memoryview некоторые параметры среза. И вы не можете использовать сам оператор в качестве аргумента, потому что это оператор, а не значение или объект.
Возможно, конструктор memoryview мог бы взять объект среза:
desired_slice_view = memoryview(existing_array, slice(1, 5, 2) )
... но это не совсем удовлетворительно, поскольку пользователям нужно будет узнать об объекте среза и о том, что означают параметры его конструктора, когда они уже думают в терминах нотации оператора среза.
источник
Вот код python3.
#!/usr/bin/env python3 import time for n in (100000, 200000, 300000, 400000): data = b'x'*n start = time.time() b = data while b: b = b[1:] print ('bytes {:d} {:f}'.format(n,time.time()-start)) for n in (100000, 200000, 300000, 400000): data = b'x'*n start = time.time() b = memoryview(data) while b: b = b[1:] print ('memview {:d} {:f}'.format(n,time.time()-start))
источник
Отличный пример от Antimony. Фактически, в Python3 вы можете заменить data = 'x' * n на data = bytes (n) и поставить скобки для операторов печати, как показано ниже:
import time for n in (100000, 200000, 300000, 400000): #data = 'x'*n data = bytes(n) start = time.time() b = data while b: b = b[1:] print('bytes', n, time.time()-start) for n in (100000, 200000, 300000, 400000): #data = 'x'*n data = bytes(n) start = time.time() b = memoryview(data) while b: b = b[1:] print('memoryview', n, time.time()-start)
источник