簡體   English   中英

Python中的字符串連接與字符串替換

[英]String concatenation vs. string substitution in Python

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

請記住,風格決策實際的決定,如果你打算維護或調試你的代碼:-)有一個着名的引用Knuth(可能引用Hoare?):“我們應該忘記小的效率,大約97%的時間說:過早優化是萬惡之源。“

只要你小心不要(比方說)將O(n)任務變成O(n 2 )任務,我會選擇你最容易理解的任何一個。

我盡可能地使用替代品。 如果我在for循環中構建一個字符串,我只使用連接。

實際上正確的做法是在這種情況下(構建路徑)使用os.path.join 不是字符串連接或插值

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM