Python进阶编程:编写更高效、优雅的Python代码
上QQ阅读APP看书,第一时间看更新

2.2.5 字符串拼接

在实际应用中,我们经常需要将多个字符串合并为一个字符串。不同的字符串合并方式对执行效率的影响不同。

如果要合并的字符串是在一个序列或者迭代器中,那么最快的方式是使用join()方法,示例如下:


test_list = ['Life', 'is', 'short', 'Use', 'python']
print(' '.join(test_list))
print(','.join(test_list))

join()方法用于将序列中的元素以指定的字符连接生成一个新的字符串。这样做的原因是连接的对象可能来自不同的数据序列(比如列表、元组、字典、文件、集合或生成器等),如果在所有对象上都定义一个join()方法明显是冗余的,因此只需要指定想要的分割字符串并调用join()方法将文本片段组合起来即可。

如果只是合并少数字符串,使用加号(+)即可,代码示例如下:


a_str = 'Life is short'
b_str = 'Use python'
print(a_str + ',' + b_str)

加号(+)操作符在做一些复杂字符串格式化的时候也很有用。

如果想在代码中将两个字面字符串合并起来,只需要简单地将它们放到一起,不需要用加号(+)。

字符串合并可能看上去比较简单,但是这个问题不能轻视,程序员经常因为选择不当的字符串格式化方式,导致应用程序性能严重降低。

注意:使用加号(+)操作符去连接大量的字符串,是非常低效的。因为加号(+)连接会引起内存复制以及垃圾回收操作。特别注意的是,永远不要像下面这样写字符串连接代码:


str_val = ''
for test_str in test_list:
    str_val += test_str

这种写法比使用join()方法运行得要慢一些,每次执行“+=”操作会创建一个新的字符串对象。最好是先收集所有的字符串片段,然后再将它们连接起来。

一个相对较好的做法是在利用生成器表达式将数据转换为字符串的同时合并字符串,代码示例如下:


data_list = ['python', 23, 2020.4]
print(','.join(str(d) for d in data_list))

避免不必要的字符串连接操作,代码示例如下:


c_str = 'Let together'
# not support
print(a_str + ':' + b_str + ':' + c_str)
# not support
print(':'.join([a_str, b_str, c_str]))
# support,is better
print(a_str, b_str, c_str, sep=':')

当混合使用I/O操作和字符串连接操作的时候,有时候需要仔细研究程序。相关代码(merge_str.py)片段如下:


# Version 1 (string concatenation)
f.write(chunk1 + chunk2)

# Version 2 (separate I/O operations)
f.write(chunk1)
f.write(chunk2)

如果两个字符串很小,版本1性能会更好些,因为I/O系统调用天生就慢。如果两个字符串很大,版本2会更加高效,因为它避免了创建一个很大的临时对象及复制大量的内存块数据。具体的选择需要根据应用程序特点来决定。

如果准备编写大量小字符串输出的代码,最好选择使用生成器函数。利用yield语句产生输出片段,代码示例如下:


def sample_func():
    yield 'Life'
    yield 'is'
    yield 'short'
    yield 'Use'
    yield 'python'

这种方法有趣的一面是,它并没有对输出片段到底要怎样组织做出假设。我们可以简单地使用join()方法将这些片段拼接起来,或者将字符串片段重定向到I/O,代码示例如下:


text_val = ''.join(sample_func())

for str_val in sample_func():
    f.write(str_val)

还可以写出一些结合I/O操作的混合方案:


def combine_obj(source, max_size):
    str_list = []
    size = 0
    for item_val in source:
        str_list.append(item_val)
        size += len(item_val)
        if size > max_size:
            yield ''.join(str_list)
            str_list = []
            size = 0
    yield ''.join(str_list)

with open('file_name', 'w') as f:
    for str_val in combine_obj(sample_func(), 8192):     
        f.write(str_val)

原始的生成器函数不需要知道使用细节,只需要负责生成字符串片段。