Python 相当于 Java StringBuffer?

新手上路,请多包涵

Python 中是否有类似 Java 的 StringBuffer 的内容?由于字符串在 Python 中也是不可变的,因此在循环中编辑它们效率低下。

原文由 user2902773 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 741
1 个回答

蟒蛇3

文档

连接不可变序列总是会产生一个新对象。这意味着通过重复串联构建序列将在总序列长度中产生二次运行时间成本。要获得线性运行时成本,您必须切换到以下替代方案之一:如果连接 str 对象,您可以构建一个列表并在最后使用 str.join() 或写入 io.StringIO 实例并检索其值完成时

实验比较几个选项的运行时间:

 import sys
import timeit
from io import StringIO
from array import array

def test_concat():
    out_str = ''
    for _ in range(loop_count):
        out_str += 'abc'
    return out_str

def test_join_list_loop():
    str_list = []
    for _ in range(loop_count):
        str_list.append('abc')
    return ''.join(str_list)

def test_array():
    char_array = array('b')
    for _ in range(loop_count):
        char_array.frombytes(b'abc')
    return str(char_array.tostring())

def test_string_io():
    file_str = StringIO()
    for _ in range(loop_count):
        file_str.write('abc')
    return file_str.getvalue()

def test_join_list_compr():
    return ''.join(['abc' for _ in range(loop_count)])

def test_join_gen_compr():
    return ''.join('abc' for _ in range(loop_count))

loop_count = 80000

print(sys.version)

res = {}

for k, v in dict(globals()).items():
    if k.startswith('test_'):
        res[k] = timeit.timeit(v, number=10)

for k, v in sorted(res.items(), key=lambda x: x[1]):
    print('{:.5f} {}'.format(v, k))

结果

3.7.5 (default, Nov  1 2019, 02:16:32)
[Clang 11.0.0 (clang-1100.0.33.8)]
0.03738 test_join_list_compr
0.05681 test_join_gen_compr
0.09425 test_string_io
0.09636 test_join_list_loop
0.11976 test_concat
0.19267 test_array

蟒蛇2

Efficient String Concatenation in Python 是一篇相当陈旧的文章,它的主要说法是朴素的串联比连接慢得多,这不再有效,因为此部分已在 CPython 中进行了优化。从 文档

CPython 实现细节:如果 s 和 t 都是字符串,一些 Python 实现(例如 CPython)通常可以对 s = s + t 或 s += t 形式的赋值执行就地优化。适用时,此优化使二次运行时间的可能性大大降低。这种优化既依赖于版本又依赖于实现。对于性能敏感的代码,最好使用 str.join() 方法,该方法可确保跨版本和实现的线性串联性能一致。

我稍微调整了他们的代码并在我的机器上得到了以下结果:

 from cStringIO import StringIO
from UserString import MutableString
from array import array

import sys, timeit

def method1():
    out_str = ''
    for num in xrange(loop_count):
        out_str += `num`
    return out_str

def method2():
    out_str = MutableString()
    for num in xrange(loop_count):
        out_str += `num`
    return out_str

def method3():
    char_array = array('c')
    for num in xrange(loop_count):
        char_array.fromstring(`num`)
    return char_array.tostring()

def method4():
    str_list = []
    for num in xrange(loop_count):
        str_list.append(`num`)
    out_str = ''.join(str_list)
    return out_str

def method5():
    file_str = StringIO()
    for num in xrange(loop_count):
        file_str.write(`num`)
    out_str = file_str.getvalue()
    return out_str

def method6():
    out_str = ''.join([`num` for num in xrange(loop_count)])
    return out_str

def method7():
    out_str = ''.join(`num` for num in xrange(loop_count))
    return out_str

loop_count = 80000

print sys.version

print 'method1=', timeit.timeit(method1, number=10)
print 'method2=', timeit.timeit(method2, number=10)
print 'method3=', timeit.timeit(method3, number=10)
print 'method4=', timeit.timeit(method4, number=10)
print 'method5=', timeit.timeit(method5, number=10)
print 'method6=', timeit.timeit(method6, number=10)
print 'method7=', timeit.timeit(method7, number=10)

结果:

 2.7.1 (r271:86832, Jul 31 2011, 19:30:53)
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)]
method1= 0.171155929565
method2= 16.7158739567
method3= 0.420584917068
method4= 0.231794118881
method5= 0.323612928391
method6= 0.120429992676
method7= 0.145267963409

结论:

  • join 仍然胜过 concat,但略胜一筹
  • 列表理解比循环更快( 在构建列表时
  • 加入生成器比加入列表慢
  • 其他方法没有用(除非你正在做一些特别的事情)

原文由 georg 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏