string连接与Python中的stringreplace

在Python中,使用string连接与stringreplace的时间和地点避开了我。 由于string连接在性能上有了很大的提升,这(更多)是一个风格决定,而不是一个实际的决定?

对于一个具体的例子,应该如何处理灵活URI的构build:

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) 

编辑:也有关于joinstring列表和使用命名replace的build议。 这些是中心主题的变种,哪种方式是在哪个时间做的呢? 感谢您的回应!

根据我的机器,连接速度(显着)更快。 但是在风格上,如果业绩不重要,我愿意支付替代的代价。 那么,如果我需要格式化,就没有必要问这个问题了…除了使用插值/模板,别无select。

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

不要忘记命名replace:

 def so_question_uri_namedsub(q_num): return "%(domain)s%(questions)s/%(q_num)d" % locals() 

“由于string连接在性能上有很大提升……”

如果表演很重要,这是很好的知道。

但是,我所看到的性能问题从来没有归结为string操作。 我通常在I / O,sorting和O( n 2 )操作成为瓶颈方面遇到麻烦。

在string操作是性能限制器之前,我会坚持明显的事情。 大多数情况下,这是一行或更less的replace,有意义的连接,以及大的模板工具(如Mako)。

小心串联string循环! string连接的代价与结果的长度成正比。 循环引导你直奔N平方的土地。 一些语言会优化连接到最近分配的string,但指望编译器将二次algorithm优化到线性是很冒险的。 最好使用带有整个string列表的原语( join ?),进行一次分配,然后将它们连在一起。

你想要连接/内插,以及如何格式化结果应该推动你的决定。

  • string插值允许您轻松添加格式。 事实上,你的string插值版本不能和你的连接版本做同样的事情; 它实际上在q_num参数之前添加了一个额外的正斜杠。 为了做同样的事情,你将不得不在这个例子中写return DOMAIN + QUESTIONS + "/" + str(q_num)

  • 插值可以更容易地格式化数字; "%d of %d (%2.2f%%)" % (current, total, total/current)连接forms的可读性要低得多。

  • 当您没有固定数量的string时,连接是有用的。

另外,知道Python 2.6引入了一个新的string插值版本,称为string模板 :

 def so_question_uri_template(q_num): return "{domain}/{questions}/{num}".format(domain=DOMAIN, questions=QUESTIONS, num=q_num) 

string模板最终会取代%插值,但是我认为这不会发生一段时间。

我只是出于好奇而testing了不同string连接/replace方法的速度。 关于这个问题的谷歌search把我带到这里。 我想我会张贴我的testing结果,希望它可以帮助别人决定。

  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) ,我发现%方法比其他小string快两倍。 concat方法总是最慢(几乎没有)。 在format()方法中切换位置时有很小的差异,但切换位置总是比常规格式方法至less慢0.01。

testing结果样本:

  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) 

我跑这些,因为我在脚本中使用string连接,我想知道是什么成本。 我按照不同的顺序来运行它们,以确保没有任何干扰,或者获得更好的性能。 在一个侧面说明中,我向像"%s" + ("a" * 1024)这样的函数中投入了一些更长的string生成器,而常规concat几乎是使用format%方法的3倍(1.1 vs 2.8)。 我想这取决于string,你试图达到什么目的。 如果performance真的很重要,那么尝试不同的东西并testing它们可能会更好。 我倾向于select速度可读性,除非速度成为问题,但那只是我。 所以不喜欢我的复制/粘贴,我不得不把所有东西放在8个空格上,以使它看起来正确。 我通常使用4。

请记住,如果您打算维护或debugging您的代码,风格决定实际的决定:-) Knuth有一句名言(可能引用Hoare?):“我们应该忘记效率很低,大约97%的时间:过早的优化是万恶之源。“

只要你小心不要(比如说)把一个O(N)任务变成一个O(N 2 )任务,我会随你find最容易理解的地方去。

我尽可能地使用替代品。 我只使用串联,如果我build立一个string说for循环。

实际上,在这种情况下(构buildpath)要做的是使用os.path.join 。 不是string连接或插值