在Python中,使用字符串连接和字符串替换的位置和时间都不包括在内.由于字符串连接在性能上有很大的提升,这是一种风格决定而不是实际的决定吗?
举个具体的例子,如何处理灵活URI的构造:
DOMAIN = 'http://stackoverflow.com' QUESTIONS = '/questions' def so_question_uri_sub(q_num): return "%s%s/%d" % (DOMAIN, QUESTIONS, q_num) def so_question_uri_cat(q_num): return DOMAIN + QUESTIONS + '/' + str(q_num)
编辑:还有关于加入字符串列表和使用命名替换的建议.这些是中心主题的变体,哪种方式是正确的方式来做到这一点?谢谢你的回复!
根据我的机器,连接速度(显着)更快.但从风格上来说,如果表现并不重要,我愿意支付替代价格.好吧,如果我需要格式化,甚至不需要问这个问题......除了使用插值/模板之外别无选择.
>>> import timeit >>> def so_q_sub(n): ... return "%s%s/%d" % (DOMAIN, QUESTIONS, n) ... >>> so_q_sub(1000) 'http://stackoverflow.com/questions/1000' >>> def so_q_cat(n): ... return DOMAIN + QUESTIONS + '/' + str(n) ... >>> so_q_cat(1000) 'http://stackoverflow.com/questions/1000' >>> t1 = timeit.Timer('so_q_sub(1000)','from __main__ import so_q_sub') >>> t2 = timeit.Timer('so_q_cat(1000)','from __main__ import so_q_cat') >>> t1.timeit(number=10000000) 12.166618871951641 >>> t2.timeit(number=10000000) 5.7813972166853773 >>> t1.timeit(number=1) 1.103492206766532e-05 >>> t2.timeit(number=1) 8.5206360154188587e-06 >>> def so_q_tmp(n): ... return "{d}{q}/{n}".format(d=DOMAIN,q=QUESTIONS,n=n) ... >>> so_q_tmp(1000) 'http://stackoverflow.com/questions/1000' >>> t3= timeit.Timer('so_q_tmp(1000)','from __main__ import so_q_tmp') >>> t3.timeit(number=10000000) 14.564135316080637 >>> def so_q_join(n): ... return ''.join([DOMAIN,QUESTIONS,'/',str(n)]) ... >>> so_q_join(1000) 'http://stackoverflow.com/questions/1000' >>> t4= timeit.Timer('so_q_join(1000)','from __main__ import so_q_join') >>> t4.timeit(number=10000000) 9.4431309007150048
不要忘记命名替换:
def so_question_uri_namedsub(q_num): return "%(domain)s%(questions)s/%(q_num)d" % locals()
警惕在循环中连接字符串! 字符串连接的成本与结果的长度成比例.循环使您直接进入N平方的土地.有些语言会优化连接到最近分配的字符串,但依靠编译器将二次算法优化为线性是有风险的.最好使用join
带有整个字符串列表的原语(?),进行单个分配,并将它们连接在一起.
"由于字符串连接在性能上有很大的提升......"
如果性能很重要,这很有用.
但是,我见过的性能问题从来没有归结为字符串操作.我通常遇到I/O问题,排序和O(n 2)操作是瓶颈.
在字符串操作是性能限制器之前,我会坚持使用明显的东西.大多数情况下,当它是一行或更少时,它是替换,当它是有意义的时候是串联,当它很大时,它是模板工具(如Mako).
您想要连接/插入的内容以及您希望如何格式化结果将决定您的决定.
字符串插值允许您轻松添加格式.实际上,您的字符串插值版本与您的串联版本不同; 它实际上在q_num
参数之前添加了一个额外的正斜杠.要做同样的事情,你必须return DOMAIN + QUESTIONS + "/" + str(q_num)
在那个例子中写.
插值可以更容易地格式化数字; "%d of %d (%2.2f%%)" % (current, total, total/current)
在连接形式中可读性要差得多.
当您没有固定数量的项目进行字符串化时,连接很有用.
另外,要知道Python 2.6引入了一个新版本的字符串插值,称为字符串模板:
def so_question_uri_template(q_num): return "{domain}/{questions}/{num}".format(domain=DOMAIN, questions=QUESTIONS, num=q_num)
字符串模板最终将取代%-interpolation,但我认为这种情况不会发生很长一段时间.
出于好奇,我只是测试不同字符串连接/替换方法的速度.关于这个主题的谷歌搜索把我带到了这里.我以为我会发布我的测试结果,希望它可以帮助某人做出决定.
import timeit def percent_(): return "test %s, with number %s" % (1,2) def format_(): return "test {}, with number {}".format(1,2) def format2_(): return "test {1}, with number {0}".format(2,1) def concat_(): return "test " + str(1) + ", with number " + str(2) def dotimers(func_list): # runs a single test for all functions in the list for func in func_list: tmr = timeit.Timer(func) res = tmr.timeit() print "test " + func.func_name + ": " + str(res) def runtests(func_list, runs=5): # runs multiple tests for all functions in the list for i in range(runs): print "----------- TEST #" + str(i + 1) dotimers(func_list)
...运行后runtests((percent_, format_, format2_, concat_), runs=5)
,我发现%方法在这些小字符串上的速度是其他方法的两倍.concat方法总是最慢的(几乎没有).在切换format()
方法中的位置时存在非常微小的差异,但切换位置始终至少比常规格式方法慢.01.
测试结果样本:
test concat_() : 0.62 (0.61 to 0.63) test format_() : 0.56 (consistently 0.56) test format2_() : 0.58 (0.57 to 0.59) test percent_() : 0.34 (0.33 to 0.35)
我运行这些因为我在脚本中使用字符串连接,我想知道成本是多少.我以不同的顺序运行它们以确保没有任何干扰,或者首先或最后获得更好的性能.在旁注中,我将一些较长的字符串生成器投入到这些函数中,"%s" + ("a" * 1024)
并且常规concat几乎是使用format
和%
方法的3倍(1.1 vs 2.8).我想这取决于字符串,以及你想要实现的目标.如果性能真的很重要,那么尝试不同的东西并测试它们会更好.我倾向于选择速度可读性,除非速度成为一个问题,但那只是我.所以不喜欢我的复制/粘贴,我不得不在所有东西上放置8个空格以使其看起来正确.我通常使用4.