Путаница о StringIO, cStringIO и ByteIO


Я погуглил, а также поискал на SO разницу между этими буферными модулями. Тем не менее, я все еще не очень хорошо понимаю, и я думаю, что некоторые из сообщений, которые я читаю, устарели.

В Python 2.7.11 я загрузил двоичный файл определенного формата с помощью r = requests.get(url). Потом я прошел мимо.StringIO.StringIO(r.content), cStringIO.StringIO(r.content) и io.BytesIO(r.content) к функции, предназначенной для разбора содержимого.

Все эти три метода доступны. Я имею в виду, что даже если файл двоичный, его все равно можно использовать StringIO. Почему? Другое дело-их эффективность.
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

Как показано выше, cStringIO > StringIO > BytesIO.

Я нашел кого-то, кто упоминал, что io.BytesIO всегда делает новую копию, которая стоит больше времени. Но есть также некоторые сообщения, упоминающие, что это было исправлено в более поздних версиях Python.

Итак, может ли кто-нибудь провести тщательное сравнение между этими IOs, в обоих последних Python 2.x и 3.Икс?


Некоторые ссылки I найдено:

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

    Ио.StringIO требует строку unicode. ио.BytesIO требует строку байтов. StringIO.StringIO позволяет либо Юникод строк или байт. cStringIO.StringIO требует строку, которая кодируется как строка байтов.

Но cStringIO.StringIO('abc') не вызывает никаких ошибок.

  • Https://review.openstack.org/#/c/286926/1

    Класс StringIO - это неправильно использовать для этого класс, особенно учитывая, что субъединица v2 является двоичной, а не Строковой.

  • Http://comments.gmane.org/gmane.comp.python.devel/148717

    CStringIO.StringIO (b'data') не копировал данные во время ввода-вывода.BytesIO (b'data') делает копию (даже если данные не будут изменены позже).

В этом посте в 2014 году есть исправление.

  • множество должностей не указаны здесь.

Вот результаты Python 2.7 для примера Эрика

%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

Что касается 2.7, то cStringIO.StringIO и StringIO.StringIO гораздо эффективнее, чем io.

1 16

1 ответ:

Вы должны использовать io.StringIO или io.BytesIO, в зависимости от того, являются ли ваши данные двоичными, как в python 2, так и в python 3, для совместимости с форвардами (это все, что может предложить 3).


Вот лучший тест (для python 2 и 3), который не включает затраты на конвертацию из numpy в 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

А затем:

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

В python 2 вы также можете проверить:

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

Некоторые из них рухнут, жалуясь на символы, не являющиеся ascii


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

Результаты Python 2.7 (запуск на другой машине):

>>> %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