Unterschied zwischen `open` und` io.BytesIO` in binären Streams

73

Ich lerne, wie man mit Streams in Python arbeitet, und habe festgestellt, dass in den E / A- Dokumenten Folgendes steht:

Der einfachste Weg, einen Binärstrom zu erstellen, ist open () mit 'b' in der Moduszeichenfolge:

f = open("myfile.jpg", "rb")

In-Memory-Binärströme sind auch als BytesIO-Objekte verfügbar:

f = io.BytesIO(b"some initial binary data: \x00\x01")

Was ist der Unterschied zwischen fwie definiert durch openund fwie definiert durch BytesIO. Mit anderen Worten, was macht einen "In-Memory-Binärstrom" aus und wie unterscheidet sich das von dem, was es opentut?

Luke Whyte
quelle

Antworten:

99

Betrachten wir der Einfachheit halber zunächst das Schreiben statt das Lesen.

Also, wenn Sie open()wie sagen:

with open("test.dat", "wb") as f:
    f.write(b"Hello World")
    f.write(b"Hello World")
    f.write(b"Hello World")

Nach dem Ausführen wird eine aufgerufene Datei test.daterstellt, die 3x enthält Hello World. Die Daten werden nach dem Schreiben in die Datei nicht gespeichert (es sei denn, sie werden unter einem Namen gespeichert).

Nun, wenn Sie io.BytesIO()stattdessen überlegen :

with io.BytesIO() as f:
    f.write(b"Hello World")
    f.write(b"Hello World")
    f.write(b"Hello World")

Anstatt den Inhalt in eine Datei zu schreiben, wird er in einen In-Memory-Puffer geschrieben. Mit anderen Worten, ein Stück RAM. Im Wesentlichen wäre das Folgende das Äquivalent:

buffer = b""
buffer += b"Hello World"
buffer += b"Hello World"
buffer += b"Hello World"

In Bezug auf das Beispiel mit der with-Anweisung würde es am Ende auch eine geben del buffer.

Der Hauptunterschied ist hier die Optimierung und Leistung. io.BytesIOist in der Lage, einige Optimierungen vorzunehmen, die es schneller machen, als einfach alle b"Hello World"nacheinander zu verketten .

Nur um es zu beweisen, hier ein kleiner Maßstab:

  • Concat: 1,3529 Sekunden
  • BytesIO: 0,0090 Sekunden

import io
import time

begin = time.time()
buffer = b""
for i in range(0, 50000):
    buffer += b"Hello World"
end = time.time()
seconds = end - begin
print("Concat:", seconds)

begin = time.time()
buffer = io.BytesIO()
for i in range(0, 50000):
    buffer.write(b"Hello World")
end = time.time()
seconds = end - begin
print("BytesIO:", seconds)

Neben dem Leistungsgewinn hat die Verwendung BytesIOanstelle der Verkettung den Vorteil, dass sie anstelle eines Dateiobjekts BytesIOverwendet werden kann. Angenommen, Sie haben eine Funktion, die erwartet, dass ein Dateiobjekt geschrieben wird. Dann können Sie ihm diesen speicherinternen Puffer anstelle einer Datei geben.

Der Unterschied besteht darin, dass open("myfile.jpg", "rb")einfach der Inhalt von geladen und zurückgegeben wird myfile.jpg. Auch hier BytesIOhandelt es sich nur um einen Puffer, der einige Daten enthält.

Da BytesIOes sich nur um einen Puffer handelt - wenn Sie den Inhalt später in eine Datei schreiben möchten - müssen Sie Folgendes tun:

buffer = io.BytesIO()
# ...
with open("test.dat", "wb") as f:
    f.write(buffer.getvalue())

Außerdem haben Sie keine Version erwähnt. Ich verwende Python 3. Bezogen auf die Beispiele: Ich verwende die with-Anweisung, anstatt sie aufzurufenf.close()

Vallentin
quelle
4
Gute Antwort; Frage erwähnt in memory streamund Sie haben verwiesen in memory buffer. Gibt es einen Unterschied in Python? Es lohnt sich, kurz darauf einzugehen. Aus englischer semantischer Sicht streamimpliziert dies einen kontinuierlichen Fluss von Bits von der Quelle zur Senke (Pushing von der Quelle), wobei der Puffer einen Cache von Bits in der Quelle impliziert, der zum schnellen Abrufen von Blöcken oder Stücken von der Quelle bereit ist (die Senke zieht von der Quelle ).
Davos
Ich habe den kleinen Benchmark auf meinem Computer ausgeführt und mit Python3.5 ein ähnliches Ergebnis erzielt. Wenn ich jedoch Python 2.7 verwende, benötigen "Concat" und "BytesIO" eine ähnliche Zeit, "Concat" ist sogar etwas besser. Stimmt etwas nicht? das macht mich verwirrt.
JenkinsY
@ Vallentin, sorry, du liegst falsch, als du gesagt hast "open (" myfile.jpg "," rb ") lädt einfach den Inhalt von myfile.jpg und gibt ihn zurück", siehe rhisimport io f = open("myfile.jpg", "rb") <class '_io.BufferedReader'> >>> isinstance(f, io.BufferedIOBase) True
Yahya Yahyaoui
14

Mit openöffnet eine Datei auf Ihrer Festplatte. Je nachdem, welchen Modus Sie verwenden, können Sie von der Festplatte lesen oder schreiben (oder beides).

Ein BytesIOObjekt ist keiner realen Datei auf der Festplatte zugeordnet. Es ist nur ein Teil des Speichers, der sich wie eine Datei verhält. Es hat dieselbe API wie ein zurückgegebenes Dateiobjekt open(mit Modus r+b, der das Lesen und Schreiben von Binärdaten ermöglicht).

BytesIO(und es ist ein enges Geschwister, StringIOdas sich immer im Textmodus befindet) kann nützlich sein, wenn Sie Daten an oder von einer API übergeben müssen, die voraussichtlich ein Dateiobjekt erhalten, die Daten jedoch lieber direkt übergeben möchten. Sie können Ihre Eingabedaten in die laden, BytesIObevor Sie sie der Bibliothek übergeben. Nach der Rückkehr können Sie BytesIOmithilfe der getvalue()Methode alle Daten abrufen, die die Bibliothek in die Datei geschrieben hat . (Normalerweise müssten Sie natürlich nur eine davon machen.)

Blckknght
quelle
-4
f = open("myfile.jpg", "rb")

Lesen Sie Bytes aus der Datei von der Festplatte und weisen Sie diesen Wert dem Objekt zu, das als 'f' bezeichnet wird und von Python im Speicher gehalten wird.

f = io.BytesIO(b"some initial binary data: \x00\x01")

Weisen Sie dem Objekt, auf das als 'f' verwiesen wird und das von Python im Speicher gehalten wird, einen Byte-Stream-Wert zu.

dunkler Mann
quelle