Python - 了解bytes、str
- 2020 年 1 月 9 日
- 筆記
Python3有兩種表示字元序列的類型:bytes和str。前者的實例包含原始的8位值,後者的實例包含Unicode字元。 Python2也有兩種表示字元序列的類型,分別叫做str和Unicode。與Python3不同的是,str實例包含原始的8位值;而unicode的實例,則包含Unicode字元。 把Unicode字元表示為二進位數據(也就是原始8位值)有許多種辦法。最常見的編碼方式就是UTF-8。但是,Python3的str實例和Python2的unicode實例都沒有和特定的二進位編碼形式相關聯。要想把Unicode字元轉換成二進位數據,就必須使用encode方法。要想把二進位數據轉換成Unicode字元,則必須使用decode方法。 編寫Python程式的時候,一定要把編碼和解碼操作放在介面最外圍來做。程式的核心部分應該使用Unicode字元類型(也就是Python3中的str、Python2中的unicode),而且不要對字元編碼做任何假設。這種辦法既可以令程式接受多種類型的文本編碼(如Latin-1、Shift JIS和Big5),又可以保證輸出的文本資訊只採用一種編碼形式(最好是UTF-8)。 由於字元類型有別,所以Python程式碼中經常會出現兩種常見的使用情境: 開發者需要原始8位值,這些8位值表示以UTF-8格式(或其他編碼形式)來編碼的字元。 開發者需要操作沒有特定編碼形式的Unicode字元。 所以,我們需要編寫兩個輔助(helper)函數,以便在這兩種情況之間轉換,使得轉換後的輸入數據能夠符合開發者的預期。
#在Python3中,我們需要編寫接受str或bytes,並總是返回str的方法: def to_str(bytes_or_str): if isinstance(bytes_or_str, bytes): value = bytes_or_str.decode('utf-8') else: value = bytes_or_str return value # Instance of str #另外,還需要編寫接受str或bytes,並總是返回bytes的方法: def to_bytes(bytes_or_str): if isinstance(bytes_or_str, str): value = bytes_or_str.encode('utf-8) else: value = bytes_or_str return value # Instance of bytes #在Python2中,需要編寫接受str或unicode,並總是返回unicode的方法: #python2 def to_unicode(unicode_or_str): if isinstance(unicode_or_str, str): value = unicode_or_str.decode('utf-8') else: value = unicode_or_str return value # Instance of unicode #另外,還需要編寫接受str或unicode,並總是返回str的方法: #Python2 def to_str(unicode_or_str): if isinstance(unicode_or_str, unicode): value = unicode_or_str.encode('utf-8') else: value = unicode_or_str reutrn vlaue # Instance of str在Python中使用原始8位值與Unicode字元時,有兩個問題需要注意。 第一個問題可能會出現在Python2裡面。如果str只包含7位ASCII字元,那麼unicode和str實例似乎就成了同一種類型。 *可以用+操作符把這種str與unicode連接起來。 *可以用等價於不等價操作符,在這種str實例與unicode實例之間進行比較。 * 在格式字元串中,可以用』%s』等形式來代表unicode實例。 這些行為意味著,在只處理7位ASCII的情境下,如果某函數接受str,那麼可以給它傳入unicode;如果某函數接受unicode,那麼也可以給它傳入str。而在Python3中,bytes與str實例則絕對不會等價,即使是空字元串也不行。所以,在傳入字元序列時必須留意其類型。 第二個問題可能會出現在Python3裡面。如果通過內置的open函數獲取了文件句柄,那麼請注意,該句柄默認會採用UTF-8編碼格式來操作文件。而在Python2中,文件操作的默認編碼格式則是二進位形式。這可能會導致程式出現怪的錯誤,對習慣了Python2的程式設計師來說更是如此。 例如,現在要向文件中隨機寫入一些二進位數據。下面這種用法在Python2中可以正常運作,但在Python3中不行。
with open('/tmp/random.bin','w') as f: f.write(os.urandom(10)) >>> TypeError: must be str, not bytes發生上述異常的原因在於,Python3給open函數添加了名為encoding的新參數,而這個新參數的默認值是』utf-8』。這樣在文件句柄上進行read和write操作時,系統就要求開發者必須傳入包含Unicode字元的str實例,而不接受包含二進位數據的bytes實例。 為了解決這個問題,我們必須用二進位寫入模式(』wb』)來開啟待操作的文件,而不能像原來那樣,採用字元寫入模式(『w』)。按照下面這種方式來使用open函數,即可同時適配Python2與Python3:
with open('/tmp/ramdom.bin','wb') as f: f.write(os.urandom(10))從文件中讀取數據的時候也有這種問題。解決辦法與寫入時相似:用』rb』模式(也就是二進位模式)打開文件,而不要用』r』模式。
要點: - 在Python3中,bytes是一種包含8位值的序列,str是一種包含Unicode字元的序列。開發者不能以>或+等操作符來混同操作bytes和str實例。 - 在Python2中,str是一種包含8位值的序列,unicode是一種包含Unicode字元的序列。如果str只含有7位ASCII字元,那麼可以通過相關的操作來同時使用str和unicode。 - 在對輸入的進行操作之前,使用輔助函數來保證字元序列的類型與開發者的期望相符(有的時候,開發者想操作以UTF-8格式來編碼的8位值,有的時候,則想操作Unicode字元)。 - 從文件中讀取二進位數據,或向其中寫入二進位數據時,總應該以』rb』或』wb』等二進位模式來開啟文件。
摘自《編寫高品質Python程式碼的59個有效方法》--第三條:了解bytes、str與unicode的區別
