Путаница о 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.
Итак, может ли кто-нибудь провести тщательное сравнение между этими IO
s, в обоих последних 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 ответ:
Вы должны использовать
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