天天看點

《Python Cookbook(第2版)中文版》——1.24 讓某些字元串大小寫不敏感

本節書摘來自異步社群《python cookbook(第2版)中文版》一書中的第1章,第1.24節,作者[美]alex martelli , anna martelli ravenscrof , david ascher ,高鐵軍 譯,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

任務

你想讓某些字元串在比較和查詢的時候是大小寫不敏感的,但在其他操作中卻保持原狀。

解決方案

最好的解決方式是,将這種字元串封裝在str的一個合适的子類中:

讨論

istr類的一些實作上的選擇很值得讨論。首先,我們在 _init _中一次性生成了小寫版本,這是因為我們認識到在istr的典型應用中,這個小寫版本将會被反複地使用。我們在一個私有的變量中儲存這個小寫版本,将其作為一個屬性,當然,也别保護得太過分了(它以一個下劃線開頭,而不是兩個下劃線),因為如果從istr再派生子類(比如,進一步對其擴充,支援大小寫不敏感的切分和替換等,正如“解決方案”注釋中所說的),istr的子類很有可能會需要通路其父類istr的一些關鍵的“實作細節”。

這裡我們沒有提供其他一些方法的大小寫不敏感的版本,如replace,因為這個例子已經清晰地展示了一種通用的建立輸入和輸出之間聯系的方式。根據應用進行特别定制的子類将提供最能夠滿足需求的功能。比如,replace方法并沒有被封裝,則我們對一個istr的執行個體調用replace,傳回的是str的執行個體,而不是istr。如果這會給你的應用帶來問題,可以将所有的傳回字元串的istr方法封裝起來,這就可以確定所有傳回的結果是istr的執行個體。基于這個目的,需要另一個單獨的助手函數,相似但不完全等同于解決方案中給出的_make_case_insensitive:

需要對所有傳回字元串的方法的名字應用這個助手函數,_make_return_istr:

字元串有約20種方法(包括一些特殊方法,比如 _add 和 mul _),需要考慮哪些方法應該被封裝起來。也可以把一些額外的方法,比如split和join(它們可能需要一些特别的處理)封裝起來,或者其他的方法,如encode和decode,對于此類方法,除非定義了一個大小寫不敏感的unicode子類型,否則無法處理它們。而實際上,針對一個特定的應用,可能不是所有的未封裝的方法都會引起問題。正如你所見的那樣,由于python字元串的方法和功能很豐富,要想用一種通用的不依賴于特定應用的方式,完全徹底地定制出一個子類型,還是要花點功夫的。

istr的實作很謹慎,主要是為了避免一些重複性的例行公事般的代碼(通常是冗長且容易滋生bug的代碼),如果我們用普通的方式重載str每一個需要的方法,在類的實作中寫上一堆def語句,很有可能就會陷入這種尴尬的境地。使用可自定義的元類或者其他的進階技術對這個例子而言也不會有什麼特别的優勢,但使用一個輔助函數來生成和安裝封裝層閉包,就可以輕易地避開問題。然後我們在兩個循環中使用該輔助函數,一個循環處理常用的方法,另一個則處理特殊的方法。這兩個循環都必須被放置在class語句之後,正如我們在解決方案中給出的代碼所示,這是因為這兩個循環需要修改istr類對象,但除非用class語句完成對istr類的聲明,否則那個類對象根本就不存在(是以當然也無法修改)。

在python 2.4中,可以重新指定函數對象的func_name屬性,在本例中,當對istr執行個體應用内省機制時,可以用這種方法讓代碼變得更加清晰和易讀。但在python 2.3中,函數對象的func_name屬性是隻讀的。是以,在本節的讨論中,我們僅僅是指出了另一種可能性,我們不想因為這個小問題失去對python 2.3的相容性。

大小寫不敏感(但仍保留了大小寫資訊)的字元串有很多用途,包括提高對使用者輸入進行解析的寬松度,在檔案系統(比如windows和macintosh的檔案系統)中查找名字包含指定字元的檔案,等等。你可能會發現,有很多地方需要“大小寫不敏感”的容器類型,比如字典、清單、集合等—它們都需要在某些場合,忽略掉key或者子項的大小寫的資訊。很明顯,一個好的方法是一次性建構出“大小寫不敏感”的比較和查詢功能;現在你的工具箱中已經增加了本節提供的解決方案,可以對字元串進行任何需要的封裝和定制,你甚至還能定制其他一些你希望具備“大小寫不敏感”能力的容器類型。

比如,一個所有子項都是字元串的清單,你希望能夠進行一些大小寫無關的處理(如用count和index進行排序),完全可以基于istr的實作,輕易地建構一個ilist:

本質上,我們做的事情是把ilist執行個體中每個子項都通過調用istr來封裝,其餘部分則保持原狀。

另外提一句,ilist的實作方式使得可以根據應用提供特定的istr,輕易地完成對子類的定制:隻需在ilist的子類中重載成員變量wrap_each_item即可。