如何清除stringio对象?

Inc*_*ito 54 python stringio

我创建了一个stringio对象,其中包含一些文本.我想清除它现有的值并重用它而不是回忆它.无论如何这样做?

Chr*_*gan 90

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.
Run Code Online (Sandbox Code Playgroud)

所以,你想要的.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.
Run Code Online (Sandbox Code Playgroud)

重要的是要注意,现在当前文件位置不变,而截断到零大小将重置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'
Run Code Online (Sandbox Code Playgroud)

如果您在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'
Run Code Online (Sandbox Code Playgroud)

所以在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'
Run Code Online (Sandbox Code Playgroud)

如果truncate在Python 2代码中使用该方法,则同时调用seek(0)(之前或之后,无关紧要)更安全,这样当您不可避免地将代码移植到Python 3时代码不会中断.还有另一个原因你应该创建一个新StringIO对象!

Python 2

>>> from timeit import timeit
>>> def truncate(sio):
...     sio.truncate(0)
...     return sio
... 
>>> def new(sio):
...     return StringIO()
... 
Run Code Online (Sandbox Code Playgroud)

当为空时,使用StringIO:

>>> from StringIO import StringIO
>>> timeit(lambda: truncate(StringIO()))
3.5194039344787598
>>> timeit(lambda: new(StringIO()))
3.6533868312835693
Run Code Online (Sandbox Code Playgroud)

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

>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
4.3437709808349609
>>> timeit(lambda: new(StringIO('abc' * 1000)))
4.7179079055786133
Run Code Online (Sandbox Code Playgroud)

和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
Run Code Online (Sandbox Code Playgroud)

因此,忽略潜在的内存问题(del oldstringio),截断a StringIO.StringIO(空3%快,3KB数据快8%)更快,但创建新的速度更快("紧固"也cStringIO.StringIO更快) 3KB数据快10%).所以我建议只使用最简单的一个 - 所以假设你正在使用CPython,使用cStringIO并创建新的.

Python 3

相同的代码,只需seek(0)放入.

>>> def truncate(sio):
...     sio.truncate(0)
...     sio.seek(0)
...     return sio
... 
>>> def new(sio):
...     return StringIO()
...
Run Code Online (Sandbox Code Playgroud)

空的时候:

>>> from io import StringIO
>>> timeit(lambda: truncate(StringIO()))
0.9706327870007954
>>> timeit(lambda: new(StringIO()))
0.8734330690022034
Run Code Online (Sandbox Code Playgroud)

有3KB的数据:

>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
3.5271066290006274
>>> timeit(lambda: new(StringIO('abc' * 1000)))
3.3496507499985455
Run Code Online (Sandbox Code Playgroud)

因此,对于Python 3而言,创建一个新的而不是重用一个空白的那个快11%并且创建一个新的而不是重用3K的那个快5%.再次,创建一个新的StringIO而不是截断和寻求.

  • @Peter:我认为时间很有用,因为它们(至少使用cStringIO)证明了我的期望:你最好创建一个新的StringIO而不是截断现有的StringIO. (4认同)
  • @PeterHansen我来这里很晚,但我非常感谢时间信息.它表明我的假设(也许是OP)截断会有更好的表现是没有根据的,通常我们最好开始新的.有时最好的答案表明你提出了错误的问题(特别是当他们也提供了直接的答案时). (3认同)
  • 我对此表示赞同,但对性能的关注是没有根据的,特别是当OP明确表示需要清除和重用时。哦,实际上我无论如何都会投票,因为投票应该意味着“这个答案很有用”,但我仍然认为“所以你想要截断(0)”之后的所有内容都应该被删除。 (2认同)

tst*_*077 8

有一些重要的事情要注意(至少使用Python 3.2):

求(0)IS截断之前需要(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()))
Run Code Online (Sandbox Code Playgroud)

哪个输出:

'111'
''
'\x00\x00\x00111'
Run Code Online (Sandbox Code Playgroud)

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

'111'
''
'111'
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,我*确实*需要重用相同的 StringIO 实例,因为我在单元测试中的 sys.stdout 的 @patch 修饰符中使用它,并且在调用 patch() 时无法访问实例变量,因为实例尚未创建。我在 setup() 函数中添加了eek(0) 和 truncate(0) 。谢谢! (2认同)