Überprüfen der Dokumentation zur Speicheransicht:
Mit Memoryview-Objekten kann Python-Code ohne Kopieren auf die internen Daten eines Objekts zugreifen, das das Pufferprotokoll unterstützt.
Klasse memoryview (obj)
Erstellen Sie eine Speicheransicht, die auf obj verweist. obj muss das Pufferprotokoll unterstützen. Zu den integrierten Objekten, die das Pufferprotokoll unterstützen, gehören Bytes und Bytearray.
Dann erhalten wir den Beispielcode:
>>> v = memoryview(b'abcefg')
>>> v[1]
98
>>> v[-1]
103
>>> v[1:4]
<memory at 0x7f3ddc9f4350>
>>> bytes(v[1:4])
b'bce'
Zitat vorbei, jetzt schauen wir uns das genauer an:
>>> 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
>>>
Also, was ich aus dem Obigen sammle:
Wir erstellen ein Memoryview-Objekt, um die internen Daten eines Pufferobjekts ohne Kopieren verfügbar zu machen. Um jedoch etwas Nützliches mit dem Objekt zu tun (indem wir die vom Objekt bereitgestellten Methoden aufrufen), müssen wir eine Kopie erstellen!
Normalerweise wird eine Speicheransicht (oder das alte Pufferobjekt) benötigt, wenn wir ein großes Objekt haben, und die Slices können auch groß sein. Die Notwendigkeit einer besseren Effizienz wäre vorhanden, wenn wir große Scheiben oder kleine Scheiben nur sehr oft herstellen.
Mit dem obigen Schema sehe ich nicht, wie es für beide Situationen nützlich sein kann, es sei denn, jemand kann mir erklären, was mir hier fehlt.
Edit1:
Wir haben einen großen Datenblock, den wir verarbeiten möchten, indem wir ihn von Anfang bis Ende durchlaufen, z. B. Token vom Anfang eines Zeichenfolgenpuffers extrahieren, bis der Puffer verbraucht ist. In C bedeutet dies, dass ein Zeiger durch den Code verschoben wird Puffer, und der Zeiger kann an jede Funktion übergeben werden, die den Puffertyp erwartet. Wie kann etwas Ähnliches in Python gemacht werden?
Die Leute schlagen Problemumgehungen vor, zum Beispiel verwenden viele String- und Regex-Funktionen Positionsargumente, mit denen das Vorrücken eines Zeigers emuliert werden kann. Hierbei gibt es zwei Probleme: Erstens ist es eine Problemumgehung, Sie müssen Ihren Codierungsstil ändern, um die Mängel zu beheben, und zweitens: Nicht alle Funktionen haben Positionsargumente, z. B. Regex-Funktionen und startswith
do, encode()
/ decode()
do.
Andere schlagen möglicherweise vor, die Daten in Blöcken zu laden oder den Puffer in kleinen Segmenten zu verarbeiten, die größer als das maximale Token sind. Okay, wir sind uns dieser möglichen Problemumgehungen bewusst, aber wir sollten in Python auf natürlichere Weise arbeiten, ohne zu versuchen, den Codierungsstil an die Sprache anzupassen - nicht wahr?
Edit2:
Ein Codebeispiel würde die Dinge klarer machen. Dies ist, was ich tun möchte und was ich angenommen habe, dass Memoryview es mir auf den ersten Blick ermöglichen würde. Verwenden wir pmview (richtige Speicheransicht) für die gesuchte Funktionalität:
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
quelle
Antworten:
Ein Grund, warum
memoryview
s nützlich sind, ist, dass sie im Gegensatz zubytes
/ in Scheiben geschnitten werden können, ohne die zugrunde liegenden Daten zu kopierenstr
.Nehmen Sie zum Beispiel das folgende Spielzeugbeispiel.
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
Auf meinem Computer bekomme ich
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
Sie können die quadratische Komplexität des wiederholten Schnitts deutlich erkennen. Selbst mit nur 400000 Iterationen ist es bereits unveränderlich. Mittlerweile ist die Memoryview-Version linear komplex und blitzschnell.
Bearbeiten: Beachten Sie, dass dies in CPython durchgeführt wurde. In Pypy bis 4.0.1 gab es einen Fehler, der dazu führte, dass Speicheransichten eine quadratische Leistung zeigten.
quelle
TypeError: memoryview: a bytes-like object is required, not 'str'
print
als Anweisung funktioniert auch in Python 3 nicht. Dieser Code wurde für Python 2 geschrieben, obwohl die für Python 3 erforderlichen Änderungen ziemlich trivial sind.str
in Python3 ist völlig anders in Python2 definiert.memoryview
Objekte eignen sich hervorragend, wenn Sie Teilmengen von Binärdaten benötigen, die nur die Indizierung unterstützen müssen. Anstatt Slices zu erstellen (und neue, möglicherweise große Objekte zu erstellen), um sie an eine andere API zu übergeben , können Sie einfach einmemoryview
Objekt nehmen.Ein solches API-Beispiel wäre das
struct
Modul. Anstatt einen Slice des großenbytes
Objekts zu übergeben, um gepackte C-Werte zu analysieren, übergeben Siememoryview
nur einen Bereich, aus dem Sie Werte extrahieren müssen.memoryview
Objekte unterstützen tatsächlich dasstruct
native Auspacken; Sie können auf einen Bereich des zugrunde liegendenbytes
Objekts mit einem Slice abzielen und dann.cast()
die zugrunde liegenden Bytes als lange Ganzzahlen, Gleitkommawerte oder n-dimensionale Listen von Ganzzahlen interpretieren. Dies ermöglicht eine sehr effiziente Interpretation des binären Dateiformats, ohne dass weitere Kopien der Bytes erstellt werden müssen.quelle
memoryview
. Sie haben es dann mit Text zu tun, nicht mit Binärdaten.Lassen Sie mich klarstellen, wo der Fehler beim Verstehen liegt.
Der Fragesteller erwartete, wie ich, in der Lage zu sein, eine Speicheransicht zu erstellen, die ein Segment eines vorhandenen Arrays auswählt (z. B. ein Byte oder ein Bytearray). Wir haben daher so etwas erwartet wie:
desired_slice_view = memoryview(existing_array, start_index, end_index)
Leider gibt es keinen solchen Konstruktor, und in den Dokumenten wird nicht klargestellt, was stattdessen zu tun ist.
Der Schlüssel ist, dass Sie zuerst eine Speicheransicht erstellen müssen, die das gesamte vorhandene Array abdeckt. Aus dieser Speicheransicht können Sie eine zweite Speicheransicht erstellen, die einen Teil des vorhandenen Arrays wie folgt abdeckt:
whole_view = memoryview(existing_array) desired_slice_view = whole_view[10:20]
Kurz gesagt, der Zweck der ersten Zeile besteht einfach darin, ein Objekt bereitzustellen, dessen Slice-Implementierung (dunder-getitem) eine Speicheransicht zurückgibt.
Das mag unordentlich erscheinen, aber man kann es auf verschiedene Arten rationalisieren:
Unsere gewünschte Ausgabe ist eine Speicheransicht, die ein Stück von etwas ist. Normalerweise erhalten wir ein geschnittenes Objekt von einem Objekt desselben Typs, indem wir den Slice-Operator [10:20] verwenden. Es gibt also einen Grund zu der Annahme, dass wir unsere gewünschte_Slice_view aus einer Speicheransicht abrufen müssen und dass der erste Schritt daher darin besteht, eine Speicheransicht des gesamten zugrunde liegenden Arrays zu erhalten.
Die naive Erwartung eines Memoryview-Konstruktors mit Start- und Endargumenten berücksichtigt nicht, dass die Slice-Spezifikation wirklich die gesamte Ausdruckskraft des üblichen Slice-Operators benötigt (einschließlich Dinge wie [3 :: 2] oder [: -4] usw.). Es gibt keine Möglichkeit, nur den vorhandenen (und verstandenen) Operator in diesem einzeiligen Konstruktor zu verwenden. Sie können es nicht an das Argument exist_array anhängen, da dadurch ein Slice dieses Arrays erstellt wird, anstatt dem Memoryview-Konstruktor einige Slice-Parameter mitzuteilen. Und Sie können den Operator selbst nicht als Argument verwenden, da es sich um einen Operator und nicht um einen Wert oder ein Objekt handelt.
Möglicherweise könnte ein Memoryview-Konstruktor ein Slice-Objekt aufnehmen:
desired_slice_view = memoryview(existing_array, slice(1, 5, 2) )
... aber das ist nicht sehr zufriedenstellend, da Benutzer etwas über das Slice-Objekt und die Bedeutung seiner Konstruktorparameter lernen müssten, wenn sie bereits in der Notation des Slice-Operators denken.
quelle
Hier ist Python3-Code.
#!/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))
quelle
Exzellentes Beispiel von Antimon. Tatsächlich können Sie in Python3 data = 'x' * n durch data = bytes (n) ersetzen und die folgenden Anweisungen in Klammern setzen, um Anweisungen zu drucken:
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)
quelle