HOWTO · Python
修復 Python 中的類位元組物件不是 STR 錯誤
本教程討論了 Python 中的 bytes-like object is required not str 錯誤。
本頁內容
Python 的 TypeError「a bytes-like object is required, not ‘str’」表示某項操作需要 bytes,卻收到文字(str)。請檢查發生錯誤的呼叫所接收的值,再依資料類型配合操作:API 需要 bytes 時,將文字編碼;需要處理文字時,則將 bytes 解碼。
了解「a bytes-like object is required, not ‘str’」TypeError
這則訊息點出了類型不相符的兩端。「Bytes-like object is required」說明該操作接受的值;「not ‘str’」則表示傳入的是文字。這類錯誤常出現在分隔符操作、二進位檔案寫入或 socket 傳送時。請找出發生錯誤的那個呼叫,並追查引數從讀取、建立到轉換的來源。
為什麼文字與二進位資料類型不相容
在 Python 3 中,str 是文字,也就是 Unicode 字元序列。bytes 則是不可變的位元組值序列,常用於編碼後的文字、檔案與網路資料。兩者是不同類型,不能互換,即使 bytes 所代表的內容是可讀文字也一樣。
編碼會使用 UTF-8 等字元編碼方式,將文字轉換為 bytes;解碼則會使用相應的編碼方式,將 bytes 解讀為文字。該選哪一種,取決於資料的意義和接收端 API 接受的類型;不加判斷就轉換,可能會掩蓋類型不符的問題或損壞資料。Python 2 的字串慣例不同,因此不應以其行為推論 Python 3 程式碼。
在資料邊界轉換文字與 Bytes
當要傳入需要 bytes 的 API 時,請對文字使用 encode()。當要將 bytes 交給文字處理流程時,請使用 decode(),並選擇與產生這些 bytes 時相同的編碼方式。
明確轉換能讓程式碼清楚呈現文字在哪裡變成 bytes,以及 bytes 又在哪裡轉回文字。若文字需要以可攜格式儲存或傳送,UTF-8 是常見選擇。在確認下一個操作接受什麼類型之前,請保留原始值的形式;這樣較容易找出類型不符的位置,也能避免把二進位內容當成文字轉換。
執行此範例:
本節中的程式碼以及顯示的標籤或輸出仍為英文。
text = "Hello, World!"
data = text.encode("utf-8")
print(data)
print(data.decode("utf-8"))
b'Hello, World!'
Hello, World!
輸出第一行前面的 b 前綴表示該值是 bytes。呼叫 str(data) 並不會反向執行編碼:它不會解碼位元組內容,而且可能會顯示像 b'Hello, World!' 這樣的表示形式。如果 bytes 內容是 UTF-8 文字,而下一個操作需要 str,請使用 data.decode("utf-8")。
修正檔案與 Socket 程式碼中的錯誤
以二進位模式開啟檔案時,讀取或寫入的值是 bytes;文字模式則會自動為你進行解碼或編碼。對二進位內容,請讓值保持為 bytes,並使用 bytes 分隔符,例如 parts = data.split(b",")。如果檔案內容是 UTF-8 文字,而且你需要執行文字操作,請先解碼,再使用文字分隔符:parts = data.decode("utf-8").split(",")。不要把讀取到的 bytes 傳給 str(),以為它會解碼檔案。
位元組導向的 socket 方法也有相同的類型邊界。sock.sendall(b"hello") 會直接傳入 bytes。如果訊息原本是文字,請明確編碼,例如 sock.sendall(message.encode("utf-8"))。這裡說明的是引數類型;實際的 socket 通訊還需要已連線的 socket,並符合對端使用的通訊協定。
選擇轉換方式前先確認值的類型
在發生錯誤的操作處,檢查 type(value),或使用 isinstance(value, bytes) 和 isinstance(value, str)。接著確認 API 預期的類型,判斷該值代表文字還是任意二進位資料,並只在資料邊界轉換一次。若是文字,請選用檔案或通訊協定所使用的編碼;若是任意二進位資料,則保留 bytes,並改用接受 bytes 的分隔符或操作。如此即可解決類型不符的問題,不必只憑列印出來的樣子猜測。