Python 相当于 Java StringBuffer?

2022-08-31 11:02:26

Python中有像Java这样的东西吗?由于字符串在Python中也是不可变的,因此在循环中编辑它们将效率低下。StringBuffer


答案 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

Python 2

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,但略有
  • 列表推导式比循环快(构建列表时)
  • 加入生成器比加入列表慢
  • 其他方法没有用(除非你正在做一些特别的事情)

答案 2

取决于您要执行的操作。如果你想要一个可变序列,内置类型就是你的朋友,从str到list再返回就像这样简单:list

 mystring = "abcdef"
 mylist = list(mystring)
 mystring = "".join(mylist)

如果你想使用for循环构建一个大字符串,pythonic的方式通常是构建一个字符串列表,然后用适当的分隔符(换行符或其他)将它们连接在一起。

否则,您还可以使用一些文本模板系统,解析器或任何最适合这项工作的专用工具。