Python中string的连接及格式化

本文中所有代码均运行在Python 2.7上

在日常编程过程之中,我们难免要去处理一些字符串(string)。Python中的字符串是不可变对象(immutable):一经创建就不能再改变。这也带来一个显而易见的副作用:每次字符串的连接都会产生新的字符串对象。

我们经常使用的字符串连接方法有+操作符连接和join连接。两者适用范围略有不同:前者的操作对象是两个字符串,而后者的操作对象是一个可迭代对象(iterable),前者的操作结果没有分隔符,而后者则可以指定分隔符。

除了这些区别之外,两者在性能上有没有区别呢?
接下来用一个例子说明:

import timeit
test_str =[ 'abc'*100 for _ in range(10**7)]
def join_test(test_str):
    return ' '.join(test_str)
def add_test(test_str):
    result = ''
    for one_str in test_str:
        result += one_str
    return result

>>> joinTimer = timeit.Timer("join_test(test_str)", "from __main__ import join_test, test_str")
>>> print "join test:{join_result}".format(join_result=str(joinTimer.timeit(number=100)))
join_test:47.199542
>>> addTimer = timeit.Timer("add_test(test_str)", "from __main__ import add_test, test_str")
>>> print "add test:{add_result}".format(join_result=str(addTimer.timeit(number=100)))
join_test:17926.728402

可见,两者的时间差距还是挺大的(鉴于这个时间过久,建议司机测试时减少字符串的长度和个数,以及重复测试的次数,以减少对于内存和CPU资源的消耗)。

那两者的性能差异的原因又是什么呢?

  1. 当时用+时,由于字符串是不可变对象,它的实际工作过程是:没执行一次+(实际是调用__add__())操作就申请开辟一块新的内存空间,将右值追加在上次操作结果的后面,一并写到新的内存空间中。因此,当进行n个字符串连接时,会产生n-1个中间过程,每个中间过程都要去申请内存块并进行写操作,而且随着内容的增多,申请内存块和写的操作会耗时更多。
  2. 当使用join时,它会预先计算好所需的内存空间,一次性申请好,将操作对象一次连接写入内存块。

由此可见,再进行大量字符串连接的时候,应该优先使用join

接下来,来一起看一下字符串格式化的问题。python中字符串常使用的方法有%str.format,其中formatpython的2.5版本引入的特性。
简单来说,两者相比的情况下format是一种更为简便的选择,比如下面的代码:
对于一个元组来说(hobbies=('nba', 'fiba', 'fifa')

>>> "my habbies are:%s"% hobbies

这种操作会是引起TypeError的,而必须写成下面的样子:

>>>  "my habbies are:%s"% (hobbies,)

的确可以工作,但非常的不pythonic,任何pythonista都忍不了。
format就没有相关的困扰。
而且,%操作要求变量和参数的位置要对应,而format这可以使用类似dict的方式去指定参数顺序。况且,format的方式显然更加直接,可读性更好。最后的原因,format是被官方推荐的方法,在今后会逐步取代%。更多内容

最后来谈一个在Python 3.6中新加的特性Formatted string literals,简称fstring,即通过f前缀来实现字符串插值:

>>> hobby = 'NBA'
>>> f"My hobby is {hobby}"
My hobby is NBA

这种字符串插值的语法在Scala和Swift中也有,算是一种语法糖,Python 3.6借鉴了其他语言并引入了这种语法,相比也是从可读性的角度来思考。更多Python 3.6新特性

你可能感兴趣的:(Python中string的连接及格式化)