什么时候使用 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,而不是加入字符串列表?的主要内容,如果未能解决你的问题,请参考以下文章

linq 加入 guid 和字符串列

Excel根据字符串列创建组ID

python中的StringIO在现实中是做啥用的?

如何在 Ms Access 2007 中使用 MySQL 视图,而不会出现字符串列的垃圾?

通过字符串列的最大值和最小值查找数据行中的值是不是相等

Pandas - 检查列表列中的字符串列是不是按行排列