什么时候使用 StringIO,而不是加入字符串列表?
Posted
技术标签:
【中文标题】什么时候使用 StringIO,而不是加入字符串列表?【英文标题】:When is StringIO used, as opposed to joining a list of strings? 【发布时间】:2011-06-11 15:30:42 【问题描述】:使用 StringIO 作为字符串缓冲区比使用列表作为缓冲区要慢。
什么时候使用StringIO?
from io import StringIO
def meth1(string):
a = []
for i in range(100):
a.append(string)
return ''.join(a)
def meth2(string):
a = StringIO()
for i in range(100):
a.write(string)
return a.getvalue()
if __name__ == '__main__':
from timeit import Timer
string = "This is test string"
print(Timer("meth1(string)", "from __main__ import meth1, string").timeit())
print(Timer("meth2(string)", "from __main__ import meth2, string").timeit())
结果:
16.7872819901
18.7160351276
【问题讨论】:
你可能是指上面的“When”而不是“Where”吗? 尝试 meth1 与列表理解。 【参考方案1】:StringIO 的主要优点是它可以在需要文件的地方使用。例如,您可以这样做(对于 Python 2):
import sys
import StringIO
out = StringIO.StringIO()
sys.stdout = out
print "hi, I'm going out"
sys.stdout = sys.__stdout__
print out.getvalue()
【讨论】:
在python 2中可以和with
一起使用吗?从我在这里看到的没有:bugs.python.org/issue1286
@Mr_and_Mrs_D 请参阅http://bugs.python.org/issue1286#msg176512,它声明它将从 2.5 开始工作。你还想要什么,沾上鲜血? :D
@MarkLawrence:不,它不会 - 重新阅读您链接的评论 - 您必须滚动您自己的上下文管理器【参考方案2】:
基于 Lennart Regebro 方法的另一种方法。 这比列表方法(meth1)更快
def meth4(string):
a = StringIO(string * 100)
contents = a.getvalue()
a.close()
return contents
if __name__ == '__main__':
from timeit import Timer
string = "This is test string"
print(Timer("meth1(string)", "from __main__ import meth1, string").timeit())
print(Timer("meth2(string)", "from __main__ import meth2, string").timeit())
print(Timer("meth3(string)", "from __main__ import meth3, string").timeit())
print(Timer("meth4(string)", "from __main__ import meth4, string").timeit())
结果(秒):
meth1 = 7.731315963647944
meth2 = 9.609279402186985
meth3 = 0.26534052061106195
meth4 = 2.915035489152274
【讨论】:
我认为在StringIO
中包装一个字符串只是为了立即将其再次转换回字符串并丢弃StringIO
对象,特别是如果您关心运行时。【参考方案3】:
好吧,我不知道我是否愿意把它称为“缓冲区”,你只是用两种复杂的方式将一个字符串乘以 100 次。这是一个简单的方法:
def meth3(string):
return string * 100
如果我们将其添加到您的测试中:
if __name__ == '__main__':
from timeit import Timer
string = "This is test string"
# Make sure it all does the same:
assert(meth1(string) == meth3(string))
assert(meth2(string) == meth3(string))
print(Timer("meth1(string)", "from __main__ import meth1, string").timeit())
print(Timer("meth2(string)", "from __main__ import meth2, string").timeit())
print(Timer("meth3(string)", "from __main__ import meth3, string").timeit())
结果证明速度更快:
21.0300650597
22.4869811535
0.811429977417
如果你想创建一堆字符串,然后加入它们,meth1() 是正确的方法。把它写到StringIO没有意义,这是完全不同的东西,即具有类似文件的流接口的字符串。
【讨论】:
【参考方案4】:如果你测量速度,你应该使用cStringIO
。
来自docs:
模块 cStringIO 提供了一个 界面类似于 StringIO 模块。大量使用 可以制作 StringIO.StringIO 对象 使用功能更高效 取而代之的是来自这个模块的 StringIO()。
但 StringIO 的重点是成为一个类似文件的对象,因为当某些东西期望这样并且您不想使用实际文件时。
编辑:我注意到你使用from io import StringIO
,所以你可能在 Python >= 3 或至少 2.6 上。单独的 StringIO 和 cStringIO 在 Py3 中消失了。不确定他们使用什么实现来提供 io.StringIO。还有io.BytesIO
。
【讨论】:
试试cStringIO
。结果:列表:17,cString:33。
io.StringIO 是一个 C 实现,如果它存在于您的平台上。如果不是,它使用 Python 实现回退。它变慢的原因是因为他正在做一些他首先不需要 StringIO 的事情。
模块 cStringIO
在 Python 3 中一直是 removed。以上是关于什么时候使用 StringIO,而不是加入字符串列表?的主要内容,如果未能解决你的问题,请参考以下文章