我创建了一个 stringio 对象,其中包含一些文本。我想清除其现有值并重用它而不是召回它。反正有这样做吗?
3 回答
TL;博士
不要费心清理它,只需创建一个新的——它会更快。
方法
蟒蛇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 可能更便宜(也更容易)。请参阅下面的基准。
蟒蛇 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.
重要的是要注意,现在当前文件位置没有改变,而截断为零大小会重置 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'
如果truncate
在 Python 2 代码中使用该方法,同时调用seek(0)
(之前或之后,没关系)更安全,这样当您不可避免地将代码移植到 Python 3 时,代码不会中断。还有另一个原因你应该只创建一个新StringIO
对象!
时代
蟒蛇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
使用 3KB 的数据,使用 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
(cStringIO.StringIO
3KB 数据的速度提高 10%)。所以我建议只使用最简单的——假设你正在使用 CPython,使用cStringIO
并创建新的。
蟒蛇 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
而不是截断和寻找。
有一些重要的事情需要注意(至少对于 Python 3.2):
在truncate (0) 之前需要seek( 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'
我如何设法优化序列中许多文件的处理(读取块,处理每个块,将处理后的流写入文件)是我重用相同的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()