2016-05-26 13 views
14

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

В 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.x?


Некоторые ссылки я нашел:

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

    io.StringIO requires a unicode string. io.BytesIO requires a bytes string. StringIO.StringIO allows either unicode or bytes string. cStringIO.StringIO requires a string that is encoded as a bytes string.

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

Существует исправление патч на этом посту в 2014 г.

  • Много сообщений SO, не перечисленных здесь.

Вот 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.

+0

Можете ли вы пометить каждый из ваших sni ppets как python 2 или python 3? – Eric

+0

@ Эрик, я сделал все свои тесты в Python 2.7.11. Кажется '(c) StringIO' заменяется на' io' в 3. Я в основном использую 2.7. Но я думаю, что для других читателей было бы целесообразно обсудить обе версии. – Lee

+1

['io'] (https://docs.python.org/2/library/io.html) также находится в python 2 – Eric

ответ

13

Вы должны использовать io.StringIO или io.BytesIO, в зависимости от того, являются ли ваши данные двоичными, как для python 2, так и для for-совместимости (это все, что может предложить 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 
+0

Итак, в 3.x' BytesIO' отличается от и намного быстрее, чем 'StringIO ', в отличие от 2.x. – Lee

+0

'io.BytesIO' и' io.StringIO' не сопоставимы, поскольку один работает только на двоичном входе, а другой работает только на строки unicode. – Eric

+0

Я дополнил 2.7 теста. Может быть, вы можете поместить их в свой пост? – Lee