web-dev-qa-db-fra.com

Confondre sur StringIO, cStringIO et ByteIO

J'ai googlé et recherche également sur SO pour la différence entre ces modules de tampon. Cependant, je ne comprends toujours pas très bien et je pense que certains des messages que j'ai lus sont obsolètes.

Dans Python 2.7.11, j'ai téléchargé un fichier binaire d'un format spécifique en utilisant r = requests.get(url). Puis j'ai passé StringIO.StringIO(r.content), cStringIO.StringIO(r.content) et io.BytesIO(r.content) vers une fonction conçue pour analyser le contenu.

Ces trois méthodes sont disponibles. Je veux dire, même si le fichier est binaire, il est toujours possible d'utiliser StringIO. Pourquoi?

Une autre chose concerne leur efficacité.

In [1]: import StringIO, cStringIO, io

In [2]: from numpy import random

In [3]: x = random.random(1000000)

In [4]: %timeit y = cStringIO.StringIO(x)
1000000 loops, best of 3: 736 ns per loop

In [5]: %timeit y = StringIO.StringIO(x)
1000 loops, best of 3: 283 µs per loop

In [6]: %timeit y = io.BytesIO(x)
1000 loops, best of 3: 1.26 ms per loop

Comme illustré ci-dessus, cStringIO > StringIO > BytesIO.

J'ai trouvé que quelqu'un mentionnait que io.BytesIO Fait toujours une nouvelle copie, ce qui coûte plus de temps. Mais il y a aussi quelques articles mentionnés qui ont été corrigés dans les versions ultérieures Python versions.

Alors, quelqu'un peut-il faire une comparaison approfondie entre ces IOs, dans les deux derniers Python 2.x et 3.x?


Certaines des références que j'ai trouvées:

  • https://trac.edgewall.org/ticket/12046

    io.StringIO nécessite une chaîne unicode. io.BytesIO nécessite une chaîne d'octets. StringIO.StringIO autorise unicode ou une chaîne d'octets. cStringIO.StringIO nécessite une chaîne qui est codée comme une chaîne d'octets.

Mais cStringIO.StringIO('abc') ne soulève aucune erreur.

Il y a un correctif dans ce post en 2014.

  • Beaucoup de SO messages non répertoriés ici.

Voici les résultats Python 2.7 pour l'exemple d'Eric

%timeit cStringIO.StringIO(u_data)
1000000 loops, best of 3: 488 ns per loop
%timeit cStringIO.StringIO(b_data)
1000000 loops, best of 3: 448 ns per loop
%timeit StringIO.StringIO(u_data)
1000000 loops, best of 3: 1.15 µs per loop
%timeit StringIO.StringIO(b_data)
1000000 loops, best of 3: 1.19 µs per loop
%timeit io.StringIO(u_data)
1000 loops, best of 3: 304 µs per loop
# %timeit io.StringIO(b_data)
# error
# %timeit io.BytesIO(u_data)
# error
%timeit io.BytesIO(b_data)
10000 loops, best of 3: 77.5 µs per loop

Quant à 2.7, cStringIO.StringIO Et StringIO.StringIO Sont bien plus efficaces que io.

21
Lee

Tu devrais utiliser io.StringIO pour la gestion des objets unicode et io.BytesIO pour gérer les objets bytes dans les deux python 2 et 3, pour la compatibilité ascendante (c'est tout ce que 3 a à offrir).


Voici un meilleur test (pour python 2 et 3), qui n'inclut pas les coûts de conversion de numpy en str/bytes

import numpy as np
import string
b_data = np.random.choice(list(string.printable), size=1000000).tobytes()
u_data = b_data.decode('ascii')
u_data = u'\u2603' + u_data[1:]  # add a non-ascii character

Puis:

import io
%timeit io.StringIO(u_data)
%timeit io.StringIO(b_data)
%timeit io.BytesIO(u_data)
%timeit io.BytesIO(b_data)

Dans python 2, vous pouvez également tester:

import StringIO, cStringIO
%timeit cStringIO.StringIO(u_data)
%timeit cStringIO.StringIO(b_data)
%timeit StringIO.StringIO(u_data)
%timeit StringIO.StringIO(b_data)

Certains d'entre eux se bloqueront, se plaignant des caractères non ascii


Résultats Python 3.5:

>>> %timeit io.StringIO(u_data)
100 loops, best of 3: 8.61 ms per loop
>>> %timeit io.StringIO(b_data)
TypeError: initial_value must be str or None, not bytes
>>> %timeit io.BytesIO(u_data)
TypeError: a bytes-like object is required, not 'str'
>>> %timeit io.BytesIO(b_data)
The slowest run took 6.79 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 344 ns per loop

Résultats Python 2.7 (exécutés sur une autre machine):

>>> %timeit io.StringIO(u_data)
1000 loops, best of 3: 304 µs per loop
>>> %timeit io.StringIO(b_data)
TypeError: initial_value must be unicode or None, not str
>>> %timeit io.BytesIO(u_data)
TypeError: 'unicode' does not have the buffer interface
>>> %timeit io.BytesIO(b_data)
10000 loops, best of 3: 77.5 µs per loop
>>> %timeit cStringIO.StringIO(u_data)
UnicodeEncodeError: 'ascii' codec cant encode character u'\u2603' in position 0: ordinal not in range(128)
>>> %timeit cStringIO.StringIO(b_data)
1000000 loops, best of 3: 448 ns per loop
>>> %timeit StringIO.StringIO(u_data)
1000000 loops, best of 3: 1.15 µs per loop
>>> %timeit StringIO.StringIO(b_data)
1000000 loops, best of 3: 1.19 µs per loop
19
Eric