Python實用技法第33篇:字符串鏈接及合併

上一篇文章: Python實用技法第32篇:對齊文本字符串
下一篇文章:

 問題

咱們想將許多小字符串合併成一個大的字符串。程序員

 解決方案

若是想要合併的字符串在一個序列或可迭代對象中,那麼將它們合併起來的最快方法就是使用join()方法。示例以下:編程

>>> parts = ['Is', 'Chicago', 'Not', 'Chicago?']
>>> ' '.join(parts)
'Is Chicago Not Chicago?'
>>> ','.join(parts)
'Is,Chicago,Not,Chicago?'
>>> ''.join(parts)
'IsChicagoNotChicago?'
>>>

初看上去語法可能顯得有些怪異,可是join()操做實際上是字符串對象的一個方法。這麼設計的部分緣由是由於想要合併在一塊兒的對象可能來自於各類不一樣的數據序列,好比列表、元組、字典、文件、集合或生成器,若是單獨在每一種序列對象中實現一個join()方法就顯得太冗餘了。所以只須要指定想要的分隔字符串,而後在字符串對象上使用join()方法將文本片斷粘合在一塊兒就能夠了。segmentfault

若是隻是想鏈接一些字符串,通常使用+操做符就足夠完成任務了:app

>>> a = 'Is Chicago'
>>> b = 'Not Chicago?'
>>> a + ' ' + b
'Is Chicago Not Chicago?'
>>>

針對更加複雜的字符串格式化操做,+操做符一樣能夠做爲format()的替代,很好地完成任務:函數

>>> print('{} {}'.format(a,b))
Is Chicago Not Chicago?
>>> print(a + ' ' + b)
Is Chicago Not Chicago?
>>>

若是打算在源代碼中將字符串字面值合併在一塊兒,能夠簡單地將它們排列在一塊兒,中間不加+操做符。示例以下:性能

>>> a = 'Hello' 'World'
>>> a
'HelloWorld'
>>>

 討論

字符串鏈接這個主題可能看起來尚未高級到要用一整節的篇幅來說解,可是程序員經常會在這個問題上作出錯誤的編程選擇,使得他們的代碼性能受到影響。設計

最重要的一點是要意識到使用+操做符作大量的字符串鏈接是很是低效的,緣由是因爲內存拷貝和垃圾收集產生的影響。特別是你毫不會想寫出這樣的字符串鏈接代碼:code

s = ''
for p in parts:
    s += p

這種作法比使用join()方法要慢上許多。主要是由於每一個+=操做都會建立一個新的字符串對象。咱們最好先收集全部要鏈接的部分,最後再一次將它們鏈接起來。orm

一個相關的技巧(很漂亮的技巧)是利用生成器表達式(見1.19節)在將數據轉換爲字符串的同時完成鏈接操做。示例以下:對象

>>> data = ['ACME', 50, 91.1]
>>> ','.join(str(d) for d in data)
'ACME,50,91.1'
>>>

對於沒必要要的字符串鏈接操做也要引發重視。有時候在技術上並不是必需的時候,程序員們也會得意忘形地使用字符串鏈接操做。例如在打印的時候:

print(a + ':' + b + ':' + c)     # Ugly
print(':'.join([a, b, c]))       # Still ugly
print(a, b, c, sep=':')          # Better

將字符串鏈接同I/O操做混合起來的時候須要對應用作仔細的分析。例如,考慮以下兩段代碼:

# Version 1 (string concatenation)
f.write(chunk1 + chunk2)
# Version 2 (separate I/O operations)
f.write(chunk1)
f.write(chunk2)

若是這兩個字符串都很小,那麼第一個版本的代碼能帶來更好的性能,這是由於執行一次I/O系統調用的固有開銷就很高。另外一方面,若是這兩個字符串都很大,那麼第二個版本的代碼會更加高效。由於這裏避免了建立大的臨時結果,也沒有對大塊的內存進行拷貝。這裏必須再次強調,你須要對本身的數據作分析,以此才能斷定哪種方法能夠得到最好的性能。

最後但也是最重要的是,若是咱們編寫的代碼要從許多短字符串中構建輸出,則應該考慮編寫生成器函數,經過yield關鍵字生成字符串片斷。示例以下:

def sample():
    yield 'Is'
    yield 'Chicago'
    yield 'Not'
    yield 'Chicago?'

關於這種方法有一個有趣的事實,那就是它不會假設產生的片斷要如何組合在一塊兒。好比說能夠用join()將它們簡單的鏈接起來:

text = ''.join(sample())

或者,也能夠將這些片斷重定向到I/O:

for part in sample():
    f.write(part)

又或者咱們能以混合的方式將I/O操做智能化地結合在一塊兒:

def combine(source, maxsize):
     parts = []
     size = 0
     for part in source:
         parts.append(part)
         size += len(part)
         if size > maxsize:
             yield ''.join(parts)
             parts = []
             size = 0
    yield ''.join(parts)

for part in combine(sample(), 32768):
    f.write(part)

關鍵在於這裏的生成器函數並不須要知道精確的細節,它只是產生片斷而已。

相關文章
相關標籤/搜索