web-dev-qa-db-ja.com

stringioオブジェクトをクリアするにはどうすればよいですか?

Stringioオブジェクトを作成しましたが、その中にテキストが含まれています。既存の値をクリアして、リコールする代わりに再利用したいと思います。とにかくこれを行うのはありますか?

53
Incognito

TL; DR

わざわざ消去するのではなく、新しいものを作成するだけです。高速です。

方法

Python 2

以下にそのようなことを見つける方法を示します。

_>>> from StringIO import StringIO
>>> dir(StringIO)
['__doc__', '__init__', '__iter__', '__module__', 'close', 'flush', 'getvalue', 'isatty', 'next', 'read', 'readline', 'readlines', 'seek', 'tell', 'truncate', 'write', 'writelines']
>>> help(StringIO.truncate)
Help on method truncate in module StringIO:

truncate(self, size=None) unbound StringIO.StringIO method
    Truncate the file's size.

    If the optional size argument is present, the file is truncated to
    (at most) that size. The size defaults to the current position.
    The current file position is not changed unless the position
    is beyond the new file size.

    If the specified size exceeds the file's current size, the
    file remains unchanged.
_

したがって、.truncate(0)が必要です。ただし、新しいStringIOを初期化する方がおそらく安価です(そして簡単です)。ベンチマークについては以下をご覧ください。

Python 3

tstone2077 のおかげで 違いを指摘 になりました。)

_>>> from io import StringIO
>>> dir(StringIO)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'getvalue', 'isatty', 'line_buffering', 'newlines', 'read', 'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines']
>>> help(StringIO.truncate)
Help on method_descriptor:

truncate(...)
    Truncate size to pos.

    The pos argument defaults to the current file position, as
    returned by tell().  The current file position is unchanged.
    Returns the new absolute position.
_

これに注意することが重要です。現在現在のファイル位置は変更されていません。一方、サイズ0に切り捨てるとPython 2バリアント。

したがって、Python 2の場合、必要なのは

_>>> from cStringIO import StringIO
>>> s = StringIO()
>>> s.write('foo')
>>> s.getvalue()
'foo'
>>> s.truncate(0)
>>> s.getvalue()
''
>>> s.write('bar')
>>> s.getvalue()
'bar'
_

Python 3でこれを行うと、期待した結果が得られません。

_>>> from io import StringIO
>>> s = StringIO()
>>> s.write('foo')
3
>>> s.getvalue()
'foo'
>>> s.truncate(0)
0
>>> s.getvalue()
''
>>> s.write('bar')
3
>>> s.getvalue()
'\x00\x00\x00bar'
_

したがって、Python 3では、位置をリセットする必要もあります。

_>>> from cStringIO import StringIO
>>> s = StringIO()
>>> s.write('foo')
3
>>> s.getvalue()
'foo'
>>> s.truncate(0)
0
>>> s.seek(0)
0
>>> s.getvalue()
''
>>> s.write('bar')
3
>>> s.getvalue()
'bar'
_

Python 2コードでtruncateメソッドを使用する場合、コードが勝つようにseek(0)を同時に(前でも後でもかまいません)呼び出す方が安全です。必然的にPython 3.に移植しても壊れない。そして、新しいStringIOオブジェクトを作成する必要がある別の理由があります。

Python 2

_>>> from timeit import timeit
>>> def truncate(sio):
...     sio.truncate(0)
...     return sio
... 
>>> def new(sio):
...     return StringIO()
... 
_

空の場合、StringIOを使用:

_>>> from StringIO import StringIO
>>> timeit(lambda: truncate(StringIO()))
3.5194039344787598
>>> timeit(lambda: new(StringIO()))
3.6533868312835693
_

StringIOを使用して3KBのデータを入力した場合:

_>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
4.3437709808349609
>>> timeit(lambda: new(StringIO('abc' * 1000)))
4.7179079055786133
_

CStringIOでも同じです:

_>>> from cStringIO import StringIO
>>> timeit(lambda: truncate(StringIO()))
0.55461597442626953
>>> timeit(lambda: new(StringIO()))
0.51241087913513184
>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
1.0958449840545654
>>> timeit(lambda: new(StringIO('abc' * 1000)))
0.98760509490966797
_

したがって、潜在的なメモリの問題(_del oldstringio_)を無視すると、_StringIO.StringIO_を切り捨てる方が高速になります(空の場合は3%高速、3KBのデータの場合は8%高速)が、より高速(「高速」)になります新しい_cStringIO.StringIO_(空の場合は8%高速、3KBのデータの場合は10%高速)を作成します。したがって、最も簡単なものだけを使用することをお勧めします。CPythonを使用していると仮定して、cStringIOを使用して新しいものを作成してください。

Python 3

seek(0)を入れただけの同じコード。

_>>> def truncate(sio):
...     sio.truncate(0)
...     sio.seek(0)
...     return sio
... 
>>> def new(sio):
...     return StringIO()
...
_

空の場合:

_>>> from io import StringIO
>>> timeit(lambda: truncate(StringIO()))
0.9706327870007954
>>> timeit(lambda: new(StringIO()))
0.8734330690022034
_

3KBのデータ:

_>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
3.5271066290006274
>>> timeit(lambda: new(StringIO('abc' * 1000)))
3.3496507499985455
_

したがって、Python 3空白のものを再利用する代わりに新しいものを作成すると11%高速になり、3Kのものを再利用する代わりに新しいものを作成すると5%高速になります。再び、新しいStringIO切り捨てて求めるよりも。

87
Chris Morgan

注意すべき重要な点があります(少なくともPython 3.2):

seek(0)[〜#〜] is [〜#〜] truncate(0)の前に必要です。以下に、seek(0)を使用しないコードを示します。

from io import StringIO
s = StringIO()
s.write('1'*3)
print(repr(s.getvalue()))
s.truncate(0)
print(repr(s.getvalue()))
s.write('1'*3)
print(repr(s.getvalue()))

どの出力:

'111'
''
'\x00\x00\x00111'

切り捨ての前にseek(0)を使用すると、予想される出力が得られます。

'111'
''
'111'
8
tstone2077

シーケンス内の多くのファイルの処理(チャンクの読み取り、各チャンクの処理、処理済みストリームのファイルへの書き込み)を最適化する方法は、同じ_cStringIO.StringIO_インスタンスを再利用することですが、常にreset()使用した後、書き込み、そしてtruncate()。これにより、現在のファイルに必要のない部分のみを切り捨てています。これにより、パフォーマンスが約3%向上したようです。これについてより詳しい専門家であれば誰でも、これが実際にメモリ割り当てを最適化するかどうかを確認できます。

_sio = cStringIO.StringIO()
for file in files:
    read_file_chunks_and_write_to_sio(file, sio)
    sio.truncate()
    with open('out.bla', 'w') as f:
        f.write(sio.getvalue())
    sio.reset()
_
2
Erik Allik