如何清除stringio对象?

时间:2010-12-02 01:16:44

标签: python stringio

3 个答案:

答案 0 :(得分:83)

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

(感谢tstone2077pointing out the difference。)

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

重要的是要注意,现在当前文件位置未更改,而截断到零大小将重置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

使用3个字符的数据,使用StringIO:

>>> 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而不是截断和寻找。

答案 1 :(得分:8)

有一些重要的事情需要注意(至少在Python 3.2中):

在截断(0)之前需要

seek(0) IS 。这是一些没有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'

在truncate之前使用seek(0),我们得到预期的输出:

'111'
''
'111'

答案 2 :(得分:2)

我如何设法优化序列中许多文件的处理(读取块,处理每个块,将处理后的流写入文件)是我重用相同的cStringIO.StringIO实例,但总是{{1使用之后,然后写入它,然后reset()。通过这样做,我只是截断了当前文件不需要的部分。这似乎让我的性能提升了约3%。任何更专家的人都可以确认这是否确实优化了内存分配。

truncate()