Recipe 1.6. Combining Strings
Recipe 1.6. 字符串的组合
Credit: Luther Blissett
问题
您须要将几个小字符串组合成一个大的字符串。
解法
要将小字符串序列组合成一个大字符串,使用字符串的 join 操作符即可。比如,有一个包含若干字符串的 list ,您想将其中包含的字符串全部连接起来,得到一个大的字符串,您可以这样做:
largeString = ''.join(pieces)
要将几个以变量形式存在的东西组合起来,字符串的格式化操作符 % 通常比较好用:
largeString = '%s%s something %s yet more' % (small1, small2, small3)
讨论
Python 的“ +”操作符能将字符串连接起来,看起来显然可以用来解决“将小字符串组合成大字符串”的问题。例如,若要将以变量形式存在的东西组合起来,如下的办法看起来非常顺理成章:
largeString = small1 + small2 + ' something ' + small3 + ' yet more'
类似的,若您有一个名为 pieces 的字符串序列,如下的办法看起来也非常顺理成章:
largeString = ''
for piece in pieces:
largeString += piece
还有一个等效的办法更为讨巧和紧凑:
import operator
largeString = reduce(operator.add, pieces, '')
然而,认识到这样一点非常重要:上述所有看起来很顺理成章的解法并不那么好——本条目的“ 解法”栏目中给出的方案要比那些方案好得多。
Python 中的字符串对象是不可改变的(immutable)。如此一来,对字符串进行的任何操作——包括连接字符串的操作——都会导致产生新的字符串对象,而不是修改现存的字符串对象。连接 N 个字符串涉及到创建并马上丢弃连接操作中的 N-1 个中间结果。因此,与生成中间结果的操作相比,无须生成中间结果、一次性产生最终结果的操作的性能要好得多。
Python 的字符串格式化操作符 % 就是这种不生成中间结果的操作,特别适用于“将几个以变量形式存在的东西(以及字面形式的常量字符串)组合到一起”的情形。对于此一类任务而言,性能并非是关键问题。然而 % 操作符与 + 操作符相比,还有其他潜在优势。一旦习惯了用法,操作符 % 更易读;而且,您无须调用 str 来把非字符串的东西(比如数字)转换为字符串,因为 format specifier(格式限定符) %s 会自己处理此类转换。操作符 % 还有一个优势就是,您可以使用 format specifiers(格式限定符)来控制结果,比如,您可以通过 format specifiers 来控制浮点数的字符串形式显示多少个数位。
---- BOX BEGIN ----
What Is "a Sequence?"
“序列”是什么?
Python 中没有名为“sequence(序列)”的特定类型,但 Python 当中经常使用这个术语。严格来讲,“序列”是指这样的一种容器(container):能够对其进行迭代操作,以一次一个的方式获得容器中有限个数的元素;支持 indexing(索引)、slicing(切割);能够作为参数传递给内建函数 len(该函数返回容器中元素的个数)。Python 的 list 就是您最经常遇到的一种“序列”,还有其他很多种序列(字符串、unicode 对象、tuple、array.array 等)。
有些东西并不需要 indexing、slicing 以及 len 能力——“一次获得一个元素”的迭代能力通常就足够用了。在这种情形下,我们说的就是“iterable(可迭代体)”;若我们重点关注的是有限数目的元素,则称之为“bounded iterable(有界的可迭代体)”。属于 iterable 但又不是序列的东西包括 dictionary(其迭代操作会以任意顺序,每次返回一个 key)、file object(其迭代操作为每次返回一个文本行),以及许多其他的东西,包括 iterator 和 generator 。只要是 iterable ,就都可用于 for 循环语句以及许多等价的语句环境(list comprehension 的 for 子句、Python 2.4 的 generator expression ,以及许多诸如 min, max, zip, sum, str.join 之类的内建设施等)。
您可以在 http://www.python.org/moin/PythonGlossary 找到 Python Glossary ,其中解释了上述及其他一些术语的概念。尽管本书的编辑尽量做到在术语的使用上能够遵循 Python Glossary ,但您还是会在本书许多地方看到“序列(sequence)”或“iterable(可迭代体)”,甚至是“list(列表)”这样的字眼,而从严格的术语概念来看,这些地方其实都应该使用“bounded iterable(有界的可迭代体)”这个术语。例如,本条目“解法”栏目一开头,我们就用到“小字符串序列”这样的字眼,而实际上任何由字符串组成的“bounded iterable”都适用所述的情形。全书到处使用“bounded iterable”的问题在于,这样做会使本书读起来更像数学教本,而不是讲编程实践的图书!由此,我们放弃了术语的精准用法,让本书保持了术语的各种“声音”并存,以略微不精准的术语用法来换取更好的可读性,而这些不精准的术语用法在其上下文中其实也是全然明晰的。
---- BOX E N D ----
若您在一个序列中放有许多小的字符串片断,那么性能就有可能非常重要的议题。对于使用了 + 或 +=(或是讨巧但等效的用法:使用内建函数 reduce)的循环,其执行时间与字符个数的平方成正比,因为“分配并填充大字符串”所需的时间堪与字符串长度成正比。幸运的是,Python 提供了很棒的替代方案。字符串对象的 join 方法接收一个字符串序列作为参数,将序列中所有元素连接起来,并在各个元素之间插入原字符串对象,由此产生一个结果字符串。例如, ''.join(pieces) 将 pieces 中所有元素连接起来,各个元素之间插入空字符串(即什么也不插入); ', '.join(pieces) 将所有元素连接起来,并在各个元素之间插入一个逗号和一个空格。要组合大字符串,这就是最快、最干净利落、最优雅、最具可读性的方法。
若字符串片断无法在同一时刻全部得到,而是通过输入或计算先后得到的,则可以用 list 作为中间数据结构来保存字符串片断(要在 list 末尾追加元素,您可以调用 list 的 append 或 extend 方法)。等最终片断齐全之后,调用 ''.join(thelist) 就可得到由所有片断连接而成的大字符串。我所知道的关于 Python 字符串的许多顺手的提示和技巧当中,我认为上述这个是最优的。一些 Python 程序运行太慢,其最常见的原因就是使用了 + 或者 += 来构建大字符串。因此,请教导您自己,不要那样做。使用本条目所推荐的 ''.join 即可。
Python 2.4 在改进上述“错用 +=”问题方面做了令人鼓舞的尝试,将错用 += 所导致的性能问题作了少许缓解。这样至少可以让一些新手或粗心的程序员少浪费一些机器时钟周期,而 ''.join 仍然比 += 快得多,为最佳选择。类似的,psyco(专门的 just-in-time[JIT] Python 编译器,可在 http://psyco.sourceforge.net/ 找到相关信息)可进一步缓解错用 += 所致的性能问题,但 ''.join 仍然还是最佳方案。
请参见
Library Reference 以及 Python in a Nutshell 中关于字符串方法、字符串格式化操作,以及 operator 模块的内容。
Recipe 1.6. 字符串的组合
Credit: Luther Blissett
问题
您须要将几个小字符串组合成一个大的字符串。
解法
要将小字符串序列组合成一个大字符串,使用字符串的 join 操作符即可。比如,有一个包含若干字符串的 list ,您想将其中包含的字符串全部连接起来,得到一个大的字符串,您可以这样做:
largeString = ''.join(pieces)
要将几个以变量形式存在的东西组合起来,字符串的格式化操作符 % 通常比较好用:
largeString = '%s%s something %s yet more' % (small1, small2, small3)
讨论
Python 的“ +”操作符能将字符串连接起来,看起来显然可以用来解决“将小字符串组合成大字符串”的问题。例如,若要将以变量形式存在的东西组合起来,如下的办法看起来非常顺理成章:
largeString = small1 + small2 + ' something ' + small3 + ' yet more'
类似的,若您有一个名为 pieces 的字符串序列,如下的办法看起来也非常顺理成章:
largeString = ''
for piece in pieces:
largeString += piece
还有一个等效的办法更为讨巧和紧凑:
import operator
largeString = reduce(operator.add, pieces, '')
然而,认识到这样一点非常重要:上述所有看起来很顺理成章的解法并不那么好——本条目的“ 解法”栏目中给出的方案要比那些方案好得多。
Python 中的字符串对象是不可改变的(immutable)。如此一来,对字符串进行的任何操作——包括连接字符串的操作——都会导致产生新的字符串对象,而不是修改现存的字符串对象。连接 N 个字符串涉及到创建并马上丢弃连接操作中的 N-1 个中间结果。因此,与生成中间结果的操作相比,无须生成中间结果、一次性产生最终结果的操作的性能要好得多。
Python 的字符串格式化操作符 % 就是这种不生成中间结果的操作,特别适用于“将几个以变量形式存在的东西(以及字面形式的常量字符串)组合到一起”的情形。对于此一类任务而言,性能并非是关键问题。然而 % 操作符与 + 操作符相比,还有其他潜在优势。一旦习惯了用法,操作符 % 更易读;而且,您无须调用 str 来把非字符串的东西(比如数字)转换为字符串,因为 format specifier(格式限定符) %s 会自己处理此类转换。操作符 % 还有一个优势就是,您可以使用 format specifiers(格式限定符)来控制结果,比如,您可以通过 format specifiers 来控制浮点数的字符串形式显示多少个数位。
---- BOX BEGIN ----
What Is "a Sequence?"
“序列”是什么?
Python 中没有名为“sequence(序列)”的特定类型,但 Python 当中经常使用这个术语。严格来讲,“序列”是指这样的一种容器(container):能够对其进行迭代操作,以一次一个的方式获得容器中有限个数的元素;支持 indexing(索引)、slicing(切割);能够作为参数传递给内建函数 len(该函数返回容器中元素的个数)。Python 的 list 就是您最经常遇到的一种“序列”,还有其他很多种序列(字符串、unicode 对象、tuple、array.array 等)。
有些东西并不需要 indexing、slicing 以及 len 能力——“一次获得一个元素”的迭代能力通常就足够用了。在这种情形下,我们说的就是“iterable(可迭代体)”;若我们重点关注的是有限数目的元素,则称之为“bounded iterable(有界的可迭代体)”。属于 iterable 但又不是序列的东西包括 dictionary(其迭代操作会以任意顺序,每次返回一个 key)、file object(其迭代操作为每次返回一个文本行),以及许多其他的东西,包括 iterator 和 generator 。只要是 iterable ,就都可用于 for 循环语句以及许多等价的语句环境(list comprehension 的 for 子句、Python 2.4 的 generator expression ,以及许多诸如 min, max, zip, sum, str.join 之类的内建设施等)。
您可以在 http://www.python.org/moin/PythonGlossary 找到 Python Glossary ,其中解释了上述及其他一些术语的概念。尽管本书的编辑尽量做到在术语的使用上能够遵循 Python Glossary ,但您还是会在本书许多地方看到“序列(sequence)”或“iterable(可迭代体)”,甚至是“list(列表)”这样的字眼,而从严格的术语概念来看,这些地方其实都应该使用“bounded iterable(有界的可迭代体)”这个术语。例如,本条目“解法”栏目一开头,我们就用到“小字符串序列”这样的字眼,而实际上任何由字符串组成的“bounded iterable”都适用所述的情形。全书到处使用“bounded iterable”的问题在于,这样做会使本书读起来更像数学教本,而不是讲编程实践的图书!由此,我们放弃了术语的精准用法,让本书保持了术语的各种“声音”并存,以略微不精准的术语用法来换取更好的可读性,而这些不精准的术语用法在其上下文中其实也是全然明晰的。
---- BOX E N D ----
若您在一个序列中放有许多小的字符串片断,那么性能就有可能非常重要的议题。对于使用了 + 或 +=(或是讨巧但等效的用法:使用内建函数 reduce)的循环,其执行时间与字符个数的平方成正比,因为“分配并填充大字符串”所需的时间堪与字符串长度成正比。幸运的是,Python 提供了很棒的替代方案。字符串对象的 join 方法接收一个字符串序列作为参数,将序列中所有元素连接起来,并在各个元素之间插入原字符串对象,由此产生一个结果字符串。例如, ''.join(pieces) 将 pieces 中所有元素连接起来,各个元素之间插入空字符串(即什么也不插入); ', '.join(pieces) 将所有元素连接起来,并在各个元素之间插入一个逗号和一个空格。要组合大字符串,这就是最快、最干净利落、最优雅、最具可读性的方法。
若字符串片断无法在同一时刻全部得到,而是通过输入或计算先后得到的,则可以用 list 作为中间数据结构来保存字符串片断(要在 list 末尾追加元素,您可以调用 list 的 append 或 extend 方法)。等最终片断齐全之后,调用 ''.join(thelist) 就可得到由所有片断连接而成的大字符串。我所知道的关于 Python 字符串的许多顺手的提示和技巧当中,我认为上述这个是最优的。一些 Python 程序运行太慢,其最常见的原因就是使用了 + 或者 += 来构建大字符串。因此,请教导您自己,不要那样做。使用本条目所推荐的 ''.join 即可。
Python 2.4 在改进上述“错用 +=”问题方面做了令人鼓舞的尝试,将错用 += 所导致的性能问题作了少许缓解。这样至少可以让一些新手或粗心的程序员少浪费一些机器时钟周期,而 ''.join 仍然比 += 快得多,为最佳选择。类似的,psyco(专门的 just-in-time[JIT] Python 编译器,可在 http://psyco.sourceforge.net/ 找到相关信息)可进一步缓解错用 += 所致的性能问题,但 ''.join 仍然还是最佳方案。
请参见
Library Reference 以及 Python in a Nutshell 中关于字符串方法、字符串格式化操作,以及 operator 模块的内容。