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’”则表示传入的是文本。此错误常见于分隔符操作、二进制文件写入或套接字发送。请找到具体出错的调用,并追踪其参数最初是从哪里读取、创建或转换而来的。

为什么文本与二进制数据不能直接匹配

在 Python 3 中,str 表示文本,是 Unicode 字符序列。bytes 是不可变的字节值序列,常用于编码后的文本、文件和网络数据。两者属于不同类型,不能互换,即使这些字节表示的是可读字符也是如此。

编码会使用 UTF-8 等字符编码将文本转换为字节;解码则使用相应的编码将字节解释为文本。该如何选择,取决于数据的含义以及接收它的 API 接受什么类型;盲目转换可能掩盖类型不匹配,或损坏数据。Python 2 的字符串约定有所不同,因此不应依据 Python 2 的行为来判断 Python 3 代码。

在边界处转换文本与字节

要将文本传给需要 bytes 的 API,请对文本使用 encode()。要将 bytes 用于文本处理,请对其使用 decode(),并选择与这些字节生成方式相符的编码。

这种转换是显式的,因此代码会清楚展示文本在哪里变成字节,以及字节在哪里重新变成文本。对于需要以通用格式存储或发送的文本,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!' 这样的表示形式。如果这些字节是 UTF-8 文本,且下一步操作需要 str,请使用 data.decode("utf-8")。

修复文件与套接字代码中的错误

以二进制模式打开的文件会返回或接受 bytes;文本模式则会自动为你执行解码或编码。处理二进制内容时,请保留其字节形式,并使用 bytes 分隔符,例如 parts = data.split(b",")。如果文件包含 UTF-8 文本且需要进行文本操作,请先解码,再使用文本分隔符:parts = data.decode("utf-8").split(",")。不要把读取到的字节传给 str(),并期待它替你解码文件。

字节导向的套接字方法也有相同的类型边界。sock.sendall(b"hello") 会直接传入 bytes。如果消息原本是文本,请显式编码,例如 sock.sendall(message.encode("utf-8"))。这里说明的是参数类型;实际的套接字通信还取决于套接字是否已连接,以及对端使用的协议。

选择转换方式前先检查值

在出错的操作处,检查 type(value),或使用 isinstance(value, bytes) 和 isinstance(value, str)。然后确认 API 需要的类型,判断该值表示文本还是任意二进制数据,并只在相应边界处转换一次。对于文本,请选择文件或协议所使用的编码;对于任意二进制数据,请保留其字节形式,并改用接受 bytes 的分隔符或操作。这样便能解决类型不匹配,无须根据打印出来的外观猜测。