1. 程式人生 > >深入理解Python字串的用法

深入理解Python字串的用法

0. 拼接字串

字串的拼接操作最常用,我專門為這個話題寫過一篇《Python拼接字串的七種方式》,建議你回看。

在此,簡單回顧一下:七種拼接方式從實現原理上劃分為三類,即格式化類(%佔位符、format()、template)、拼接類(+操作符、類元祖方式、join())與插值類(f-string),在使用上,我有如下建議——

當要處理字串列表等序列結構時,採用join()方式;拼接長度不超過20時,選用+號操作符方式;長度超過20的情況,高版本選用f-string,低版本時看情況使用format()或join()方式。

不敢說字串就只有這七種拼接方式,但應該說它們是最常見的了。有小夥伴說,我寫漏了一種,即字串乘法

,可以重複拼接自身。沒錯,從結果上看,這是第八種拼接方式,視為補充吧。

關於字串拼接,還得補充一個建議,即在複雜場景下,儘量避免使用以上幾類原生方法,而應該使用外接的強大的處理庫。比如在拼接 SQL 語句的時候,經常要根據不同的條件分支,來組裝不同的查詢語句,而且還得插入不同的變數值,所以當面臨這種複雜的場景時,傳統拼接方式只會加劇程式碼的複雜度、降低可讀性和維護性。使用SQLAlchemy模組,將有效解決這個問題。

1. 拆分字串

在字串的幾種拼接方法中,join() 方法可以將列表中的字串元素,拼接成一個長的字串,與此相反,split() 方法可以將長字串拆分成一個列表。前面已說過,字串是不可變序列,所以字串拆分過程是在拷貝的字串上進行,並不會改變原有字串。

split() 方法可接收兩個引數,第一個引數是分隔符,即用來分隔字串的字元,預設是所有的空字元,包括空格、換行( )、製表符( )等。拆分過程會消耗分隔符,所以拆分結果中不包含分隔符。


s = 'Hello world'

l = '''Hi there , my name is Python貓

Do you like me ?

'''


# 不傳引數時,預設分隔符為所有空字元

s.split() >>> ['Hello', 'world']

s.split(' ') >>> ['Hello', 'world'

]

s.split(' ') >>> ['Hello world'] # 不存在兩個空格符

s.split('world') >>> ['Hello', '']


# 空字元包括空格、多個空格、換行符等

l.split() >>> ['Hi', 'there', ',', 'my', 'name', 'is', 'Python貓', 'Do', 'you', 'like', 'me', '?']

split() 方法的第二個引數是一個數字,預設是預設,預設時全分隔,也可以用 maxsplit 來指定拆分次數。


# 按位置傳參

l.split(' ',3)

>>> ['Hi', 'there', ',', 'my name is Python 貓

Do you like me ?

']


# 指定傳參

l.split(maxsplit=3)

>>> ['Hi', 'there', ',', 'my name is Python 貓

Do you like me ?

']


# 錯誤用法

l.split(3)

---------------

TypeError Traceback (most recent call last)

<ipython-input-42-6c16d1a50bca> in <module>()

----> 1 l.split(3)

TypeError: must be str or None, not int

split() 方法是從左往右遍歷,與之相對,rsplit() 方法是從右往左遍歷,比較少用,但是會有奇效。

拆分字串還有一種方法,即 splitlines() ,這個方法會按行拆分字串,它接收一個引數 True 或 False ,分別決定換行符是否會被保留,預設值 False ,即不保留換行符。


# 預設不保留換行符

'ab c

kl

de fg

'.splitlines()

>>> ['ab c', '', 'de fg', 'kl']


'ab c

kl

de fg

'.splitlines(True)

>>> ['ab c

', '

', 'de fg

', 'kl

']

2. 替換字串

替換字串包括如下場景:大小寫替換、特定符號替換、自定義片段替換……

再次說明,字串是不可變物件,以下操作並不會改變原有字串。

196502ad081c45ca5427dd588705b752d9337de7

以上這些方法都很明瞭,使用也簡單,建議你親自試驗一下。這裡只說說 strip() 方法,它比較常用,可以去除字串前後的空格,不僅如此,它還可以刪除首末位置的指定的字元。


s = '******Hello world******'

s.strip('*') >>> 'Hello world'

3. 查詢字串

查詢字串中是否包含某些內容,這是挺常用的操作。Python 中有多種實現方式,例如內建的 find() 方法,但是這個方法並不常用,因為它僅僅告訴你所查詢內容的索引位置,而在通常情況下,這個位置並不是我們的目的。

find() 方法與 index() 方法的效果一樣,它們的最大的區別只在於,找不到內容時的返回值不同,一個返回 -1,一個丟擲異常 :


s = 'Hello world'


s.find('cat') >>> -1


s.index('cat')

>>> ValueError Traceback (most recent call last)

<ipython-input-55-442007c50b6f> in <module>()

----> 1 s.index('cat')


ValueError: substring not found

以上兩個方法,只能用來滿足最簡單的查詢需求。在實戰中,我們常常要查詢特定模式的內容,例如某種格式的日期字串,這就得藉助更強大的查詢工具了。正則表示式和 re 模組就是這樣的工具,正則表示式用來定製匹配規則,re 模組則提供了 match() 、find() 及 findall() 等方法,它們組合起來,可以實現複雜的查詢功能。限於篇幅,今後再對這兩大工具做詳細介紹,這裡有一個簡單的例子:


import re

datepat = re.compile(r'd+/d+/d+')

text = 'Today is 11/21/2018. Tomorrow is 11/22/2018.'

datepat.findall(text)

>>> ['11/21/2018', '11/22/2018']

4. 字元判斷

判斷字串是否(只)包含某些字元內容,這類使用場景也很常見,例如在網站註冊時,要求使用者名稱只能包含英文字母和數字,那麼,當校驗輸入內容時,就需要判斷它是否只包含這些字元。其它常用的判斷操作,詳列如下:

30b44f25614d18c0d4050f7135cd7ee5518dbdc4

5. 字串不可以做的事

上文內容都是 Python 字串特有的操作方法,相信讀完之後,你更清楚知道 Python 能夠做什麼了。

但是,這還不足以回答本文標題的問題——你真的知道 Python 的字串怎麼用嗎?這些特有的操作方法,再加上之前文章提到的序列共有的操作、字串讀寫檔案、字串列印、字串Intern機制等等內容,才差不多能夠回答這個問題。

儘管如此,為了體現嚴謹性,我試著再聊聊“Python 字串不可以做的事”,從相反的維度來補充回答這個問題。下面是開拓思維,進行頭腦風暴的時刻:

(1)受限的序列

與典型的序列型別相比,字串不具備列表的如下操作:append()、clear()、copy()、insert()、pop()、remove(),等等。這是為什麼呢?

有幾個很好理解,即append()、insert()、pop() 和 remove(),它們都是對單個元素的操作,但是,字串中的單個元素就是單個字元,通常沒有任何意義,我們也不會頻繁對其做增刪操作,所以,字串沒有這幾個方法也算合理。

列表的 clear() 方法會清空列表,用來節省記憶體空間,效果等於anylist[:] = [],但是,奇怪的是,Python 並不支援清空/刪除操作。

首先,字串沒有 clear() 方法,其次,它是不可變物件,不支援這種賦值操作anystr[:] = '',也不支援del anystr[:]操作:


s = 'Hello world'


s[:] = ''

>>> 報錯:TypeError: 'str' object does not support item assignment


del s[:]

>>> 報錯:TypeError: 'str' object does not support item deletion

當然,你也別想通過del s來刪除字串,因為變數名 s 只是字串物件的引用(挖坑,以後寫寫這個話題),只是一個標籤,刪除標籤並不會直接導致物件實體的消亡。

如此看來,想要手動清空/刪除 Python 字串,似乎是無解。

最後還有一個 copy() 方法,這就是拷貝嘛,可是字串也沒有這個方法。為什麼呢?難道拷貝字串的場景不多麼?在這點上,我也沒想出個所以然來,擱置疑問。

通過以上幾個常用列表操作的比較,我們可以看出字串這種序列是挺受限的。列表可以看成多節車廂連結成的火車,而字串感覺就只像多個座椅聯排成的長車廂,真是同源不同相啊。

(2)比就比,誰怕誰

接下來,又到了 Python 字串與 Java 字串 PK 的時刻。在上一篇文章《你真的瞭解Python的字串嗎?》中,它們已經在物件定義的角度切磋了兩回合,勝利的天平倒向了 Python,這次看看會比出個啥結果吧。

Java 中有比較字串的方法,即 compareTo() 方法與 equals() 方法,前一個方法逐一比較兩個字串的字元編碼,返回一個整型的差值,後一個方法在整體上比較兩個字串的內容是否相等。

Python 字串沒有這兩個單獨的方法,但要實現類似的功能卻很簡便。 先看例子:


myName = "Python貓"

cmpName = "world"

newName = myName


# 直接用比較符號進行compare

myName > cmpName

>>> False

myName == newName

>>> True

cmpName != newName

>>> True


# 比較是否同一物件

myName is cmpName

>>> False

myName is newName

>>> True

上例中,如果把賦值的字串換成列表或者其它物件,這些比較操作也是可以進行的。也就是說,作比較的能力是 Python 公民們的一項基本能力,並不會因為你是字串就給你設限,或者給你開特權。

與此類似,Python 公民們自帶求自身長度的能力,len() 方法是內建方法,可以直接傳入任意序列引數,求解長度。Java 中則要求不同的序列物件,只能呼叫各自的 length() 方法。說個形象的比喻,Python 中共用一把秤,三教九流之輩都能拿它稱重,而Java 中有多把秤,你稱你的,我稱我的,大家“井水不犯河水”。

Python 中曾經有 cmp() 方法和__cmp__()魔術方法,但官方嫌棄它們雞肋,所以在Python 3 中移除掉了。雖然在 operator 模組中還為它留下了一脈香火,但保不定哪天就會徹底廢棄。


import operator

operator.eq('hello', 'name')

>>> False

operator.eq('hello', 'hello')

>>> True

operator.gt('hello', 'name')

>>> False

operator.lt('hello', 'name')

>>> True

(3)牆上的門

在 Java 中,字串還有一個強大的 valueOf() 方法,它可以接收多種型別的引數,如boolean、char、char陣列、double、float、int等等,然後返回這些引數的字串型別。 例如,要把 int 轉為字串,可以用 String.valueOf(anynum) 。

Python 字串依然沒有這個單獨的方法,但要實現相同的功能卻很簡便。對Python來說,不同的資料型別轉換成字串,那是小菜一碟,例如:


str(123) >>> '123'

str(True) >>> 'True'

str(1.22) >>> '1.22'

str([1,2]) >>> '[1, 2]'

str({'name':'python', 'sex':'male'})

>>> "{'name': 'python', 'sex': 'male'}"

而從字串轉換為其它型別,也不難,例如,int('123') 即可由字串'123' 得到數字 123。對比 Java,這個操作要寫成 Integer.parseInt('123')

在Java 的不同資料型別之間,那道分隔之牆矗立得很高,彷彿需要藉助一座更高的吊橋才能溝通兩邊,而在靈活的 Python 裡,你可以很方便地開啟牆上的那扇門,來往穿越。

小結一下,跟 Java 相比,Python 字串確實沒有幾項方法,但是事出有因,它們的天賦能力可不弱,所有這些操作都能簡明地實現。一方面,Python 字串做不到某些事,但是另一方面,Python 可以出色地做成這些事,孰優孰劣,高下立判。

6. 總結

寫文章貴在善始善終,現在給大家總結一下:本文主要介紹 Python 字串特有的操作方法,比如它的拼接、拆分、替換、查詢及字元判斷等使用方法,從正向回答,Python 字串能做什麼?最後,我們還從反向來回答了 Python 字串不能做什麼?有些不能做,實際上是 不為,是為了在其它地方更好地作為,歸根到底,應該有的功能,Python 字串全都有了。

本文中依然將 Python 與 Java 做了比較,有幾項小小的差異,背後反映的其實是,兩套語言系統在世界觀上的差異。古人云,以銅為鏡,可以正衣冠。那麼,在程式語言的世界裡,以另一種語言為鏡,也更能看清這種語言的面貌。希望這種跨語言的思維碰撞,能為你擦出智慧的火花。


原文釋出時間為:2018-11-28

本文作者:豌豆花下貓

本文來自雲棲社群合作伙伴“ Python中文社群”,瞭解相關資訊可以關注“Python中文社群”。