2013-11-25 61 views
3

據我瞭解,「.join(iterable_of_strings)是連接字符串的首選方法,因爲它允許進行優化,避免必須將不可變對象重寫到內存中多次。爲什麼連接字符串比連接它們更快?

在表達式內部添加字符串的可靠運行速度比連接它們的速度快,適合我的中等數量的操作。

我在加入時獲得大約2.9-3.2秒的時間,在我的筆記本電腦上增加了使用Python 3.3運行此代碼的2.3-2.7秒。我無法找到一個很好的答案谷歌搜索。有人能解釋可能發生了什麼,或指導我獲得一個好的資源嗎?

import uuid 
import time 

class mock: 
    def __init__(self): 
     self.name = "foo" 
     self.address = "address" 
     self.age = "age" 
     self.primarykey = uuid.uuid4() 

data_list = [mock() for x in range(2000000)] 

def added(): 
    my_dict_list = {} 
    t = time.time() 
    new_dict = { item.primarykey: item.name + item.address + item.age for item in data_list } 
    print(time.time() - t) 

def joined(): 
    my_dict_list = {} 
    t = time.time() 
    new_dict = { item.primarykey: ''.join([item.name, item.address, item.age]) for item in data_list } 
    print(time.time() - t) 

joined() 
added() 
+1

http://stackoverflow.com/questions/3055477/how-slow-is-pythons-string-concatenation-vs-str-join – idanshmu

+1

cPython具有非常高效的就地字符串連接。我不會驚訝地發現,兩個連接操作比實現列表更快,然後執行'''.join'。 – roippi

回答

3

你所看到的時差來自創建列表以傳遞給join。雖然你可以用一個元組來取得一個小的加速,但是當只有幾個短的字符串的時候,它仍然會比只與+連接時慢。

如果你有一個可以迭代的字符串來啓動,而不是一個帶有字符串的對象作爲屬性,那將是不同的。然後你可以直接在迭代器上調用join,而不需要爲每個調用建立一個新的。

這裏的一些測試中,我與timeit模塊做:

import timeit 

short_strings = ["foo", "bar", "baz"] 
long_strings = [s*1000 for s in short_strings] 

def concat(a, b, c): 
    return a + b + c 

def concat_from_list(lst): 
    return lst[0] + lst[1] + lst[2] 

def join(a, b, c): 
    return "".join([a, b, c]) 

def join_tuple(a, b, c): 
    return "".join((a, b, c)) 

def join_from_list(lst): 
    return "".join(lst) 

def test(): 
    print("Short strings") 
    print("{:20}{}".format("concat:", 
          timeit.timeit(lambda: concat(*short_strings)))) 
    print("{:20}{}".format("concat_from_list:", 
          timeit.timeit(lambda: concat_from_list(short_strings)))) 
    print("{:20}{}".format("join:", 
          timeit.timeit(lambda: join(*short_strings)))) 
    print("{:20}{}".format("join_tuple:", 
          timeit.timeit(lambda: join_tuple(*short_strings)))) 
    print("{:20}{}\n".format("join_from_list:", 
          timeit.timeit(lambda: join_from_list(short_strings)))) 
    print("Long Strings") 
    print("{:20}{}".format("concat:", 
          timeit.timeit(lambda: concat(*long_strings)))) 
    print("{:20}{}".format("concat_from_list:", 
          timeit.timeit(lambda: concat_from_list(long_strings)))) 
    print("{:20}{}".format("join:", 
          timeit.timeit(lambda: join(*long_strings)))) 
    print("{:20}{}".format("join_tuple:", 
          timeit.timeit(lambda: join_tuple(*long_strings)))) 
    print("{:20}{}".format("join_from_list:", 
          timeit.timeit(lambda: join_from_list(long_strings)))) 

輸出:

Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 
Type "copyright", "credits" or "license()" for more information. 
>>> ================================ RESTART ================================ 
>>> 
>>> test() 
Short strings 
concat:    0.5453461176251436 
concat_from_list: 0.5185697357936024 
join:    0.7099379456477868 
join_tuple:   0.5900842397209949 
join_from_list:  0.4177281794285359 

Long Strings 
concat:    2.002303591571888 
concat_from_list: 1.8898819841869416 
join:    1.5672863477837913 
join_tuple:   1.4343144915087596 
join_from_list:  1.231374639083505 

因此,從現有列表加入始終是最快的。與+連接時,如果個別項目較短,則速度較快,但對於較長的字符串,它總是最慢。我懷疑在concatconcat_from_list之間顯示的差異來自測試代碼中函數調用中列表的解包。

+1

對。 concat_from_list比concat快的事實表明測試代碼中存在錯誤。正如你所說的那樣,拆包就是這樣。在任何情況下,這些時間中的很多時間都只是函數調用。我所做的測試表明,對於三個長字符串,「+」也更快,但我沒有那麼方便的數據。 –

+0

有趣的看到和感謝徹底的答覆。我絕對會採用你的時間而不是我使用的那種笨拙的成語。 –

+2

@TimWilder不,不要。如果您關心如何讓代碼更快運行,請將其分析。如果使用正確,'timeit'是一個有用的工具,但是使用不正確並不容易,而且您沒有用實際數據測試實際的運行代碼,這意味着最好能夠幫助您查看哪些類型的更改是值得一試,一旦你發現代碼很慢。 –

5

據我瞭解,「.join(iterable_of_strings)是連接字符串的首選方法,因爲它允許優化,避免必須將不可變對象重寫到內存中多次。

你有點不明白。由於你解釋的原因,"".join(iterable_of_strings)是連接字符串的迭代的首選方式。

但是,您沒有可迭代的字符串。你只有三個字符串。連接三個字符串的最快方法是將它們與+一起添加,或使用.format()%。這是因爲你的情況必須首先創建迭代器,然後加入字符串,以避免複製一些非常小的字符串。

.join()不會變得更快,直到你有這麼多的字符串,它使愚蠢的代碼無論如何使用其他方法。當這種情況發生時,取決於你有什麼Python實現,什麼版本和字符串有多長,但我們通常談論的是十多個字符串。

雖然不是所有的實現都有快速連接,但我已經測試了CPython,PyPy和Jython,並且它們所有的連接速度都比較快,或者對於幾個字符串來說速度都很快。

實際上,根據代碼清晰度,您應該在+.join()之間進行選擇,直到您的代碼運行時爲止。那麼,如果你關心速度:配置文件和基準你的代碼。不要坐下來猜測。

一些計時:http://slides.colliberty.com/DjangoConEU-2013/#/step-40

隨着視頻說明:http://youtu.be/50OIO9ONmks?t=18m30s