私はグーグルで検索し、SOでこれらのバッファモジュールの違いを検索しました。しかし、私はまだよく理解しておらず、読んだ記事のいくつかは古くなっていると思います。
Python 2.7.11では、r = requests.get(url)
を使用して特定の形式のバイナリファイルをダウンロードしました。その後、StringIO.StringIO(r.content)
、cStringIO.StringIO(r.content)
、およびio.BytesIO(r.content)
をコンテンツの解析用に設計された関数に渡しました。
これら3つの方法はすべて使用可能です。つまり、ファイルがバイナリであっても、StringIO
を使用することはまだ可能です。どうして?
もう1つは、効率性に関するものです。
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バージョンで修正されたという言及もあります。
だから、最新のPython 2.xと3.xの両方で、これらのIO
sを徹底的に比較できますか?
私が見つけた参考文献のいくつか:
io.StringIOにはUnicode文字列が必要です。 io.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 ')はデータをコピーしませんでしたが、io.BytesIO(b'data')はコピーを作成します(データが後で変更されない場合でも)。
2014年のこの投稿には修正パッチがあります。
Ericの例の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
よりもはるかに効率的です。
unicode
オブジェクトの処理には_io.StringIO
_を使用し、前方互換性の場合はpython 2および3の両方でbytes
オブジェクトの処理に_io.BytesIO
_を使用する必要があります(これは3つすべてが提供するものです)。
以下は、numpyからstr
/bytes
への変換コストを含まない、より良いテスト(python 2および3))です。
_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
_