web-dev-qa-db-fra.com

Différence entre `open` et` io.BytesIO` dans les flux binaires

J'apprends à utiliser des flux dans Python et j'ai remarqué que les IO docs disent ce qui suit:

Le moyen le plus simple de créer un flux binaire est d'utiliser open () avec 'b' dans la chaîne de mode:

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

Les flux binaires en mémoire sont également disponibles en tant qu’objets BytesIO:

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

Quelle est la différence entre f tel que défini par open et f tel que défini par BytesIO. En d’autres termes, en quoi un "flux binaire en mémoire" est-il différent de ce que fait open?

44
Luke Whyte

Par souci de simplicité, considérons l'écriture au lieu de la lecture pour l'instant.

Donc, quand vous utilisez open() comme:

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

Après avoir exécuté ce fichier, un fichier nommé test.dat sera créé, contenant Hello World. Les données ne seront pas conservées en mémoire après leur écriture dans le fichier (sauf si elles sont conservées sous un nom).

Maintenant, quand vous considérez io.BytesIO() à la place:

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

Au lieu d'écrire le contenu dans un fichier, il est écrit dans une mémoire tampon. En d'autres termes, un morceau de RAM. Essentiellement, écrire ce qui suit serait l’équivalent:

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

Par rapport à l'exemple avec l'instruction with, à la fin il y aurait aussi un del buffer.

La principale différence est l’optimisation et la performance. io.BytesIO est capable d'effectuer certaines optimisations qui le rendent plus rapide que la simple concaténation de tous les b"Hello World" un par un.

Juste pour le prouver, voici un petit repère:

  • Concat: 1.3529 secondes
  • OctetsIO: 0,0090 seconde
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)

Outre le gain de performances, l'utilisation de BytesIO au lieu de la concaténation présente l'avantage que BytesIO peut être utilisé à la place d'un objet fichier. Supposons donc que vous ayez une fonction qui attend l’écriture d’un objet de fichier. Ensuite, vous pouvez lui donner ce tampon en mémoire à la place d'un fichier.

La différence est que open("myfile.jpg", "rb") charge et renvoie simplement le contenu de myfile.jpg; alors que BytesIO encore n’est qu’un tampon contenant des données.

Puisque BytesIO n'est qu'un tampon - si vous voulez écrire le contenu ultérieurement dans un fichier - vous devrez faire:

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

De plus, vous n'avez pas mentionné de version; j'utilise Python 3. En rapport avec les exemples: j'utilise l'instruction with au lieu d'appeler f.close()

60
Vallentin

Utiliser open ouvre un fichier sur votre disque dur. Selon le mode utilisé, vous pouvez lire ou écrire (ou les deux) à partir du disque.

Un objet BytesIO n'est associé à aucun fichier réel sur le disque. C'est juste une partie de la mémoire qui se comporte comme un fichier. Elle a la même API qu'un objet fichier retourné par open (avec le mode r+b, permettant la lecture et l'écriture de données binaires).

BytesIO (et son frère proche StringIO toujours en mode texte) peut être utile lorsque vous devez transmettre des données à ou à partir d'une API qui s'attend à recevoir un objet fichier, mais préférez transmettre les données directement. Vous pouvez charger vos données d’entrée dans la BytesIO avant de les transmettre à la bibliothèque. Après son retour, vous pouvez obtenir toutes les données que la bibliothèque a écrites dans le fichier à partir de BytesIO à l'aide de la méthode getvalue(). (Habituellement, vous n’avez besoin que de l’une d’elles, bien sûr.)

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

lit les octets du fichier du disque et assigne cette valeur à l'objet référencé comme 'f' qui est conservé par Python en mémoire.

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

assigne une valeur de flux d'octets à l'objet référencé comme 'f' qui est conservé par Python en mémoire.

0
szerszen