Python 中 str、bytes、bytearray 有什么区别?
简化版
str 表示 Unicode 文本,bytes 表示不可变的字节序列,bytearray 表示可变字节序列。文本和字节之间需要通过编码转换:str.encode() 把文本编码成 bytes,bytes.decode() 把 bytes 解码成文本。
详细版
基本区别:
| 类型 | 含义 | 是否可变 | 典型场景 |
|---|---|---|---|
str | 文本字符串 | 不可变 | 业务文本、JSON 字段、日志文本 |
bytes | 字节序列 | 不可变 | 网络传输、文件二进制、加密摘要 |
bytearray | 字节序列 | 可变 | 需要原地修改二进制数据 |
编码和解码:
s = "你好"
b = s.encode("utf-8")
print(b) # b'\xe4\xbd\xa0\xe5\xa5\xbd'
text = b.decode("utf-8")
print(text) # 你好
常见错误:
# "你好" + b"world" # TypeError
str 和 bytes 不能直接拼接,必须明确编码或解码。
完整版教学
一、先分清三层:字符、编码、字节
这道题的核心不是背 str、bytes、bytearray 三个定义,而是分清“文本含义”和“底层字节”不是一回事。str 站在字符层,表示人能理解的文本;bytes 站在字节层,表示网络、磁盘、加密算法看到的原始数据;编码就是把字符映射成字节的规则。
文本字符 str --encode("utf-8")--> 字节 bytes
字节 bytes --decode("utf-8")--> 文本字符 str
同一个字符在不同编码下可能对应不同字节,同一串字节用错误编码解码也可能报错或变成乱码。面试回答时一定要说清楚:Python 3 的 str 不是“某种编码的字符串”,它是 Unicode 文本;只有落到文件、网络、数据库驱动边界时,才需要明确编码成字节。
二、str 是文本,不是字节
Python 3 中,str 表示 Unicode 文本:
s = "Python你好"
它关注的是字符语义,而不是底层如何存成字节。一个字符到底占几个字节,取决于编码方式。
例如:
len("你") # 1 个字符
len("你".encode("utf-8")) # 3 个字节
len("你".encode("gbk")) # 2 个字节
这说明“字符长度”和“字节长度”是两回事。
| 表达式 | 含义 | 典型结果 |
|---|---|---|
len("A") | 字符数 | 1 |
len("A".encode("utf-8")) | UTF-8 字节数 | 1 |
len("你") | 字符数 | 1 |
len("你".encode("utf-8")) | UTF-8 字节数 | 3 |
三、bytes 是不可变字节序列
bytes 用 b 前缀表示:
data = b"abc"
print(data[0]) # 97
注意,索引 bytes 得到的是整数,不是长度为 1 的 bytes。
bytes 常用于:
- socket 网络收发;
- HTTP body;
- 图片、音频、压缩包等二进制文件;
- 哈希、加密、签名;
- 编码后的文本。
因为 bytes 不可变,不能原地改某个位置:
# data[0] = 65 # TypeError
bytes 的元素范围是 0 到 255,因为一个字节最多表示 256 种值。b"abc" 里 a 的 ASCII 码是 97,所以 data[0] 得到 97;如果想得到长度为 1 的 bytes,要用切片 data[:1]。这点经常考,因为很多人以为索引 bytes 会得到 b"a"。
data = b"abc"
print(data[0]) # 97
print(data[:1]) # b'a'
四、bytearray 是可变字节序列
如果需要修改字节内容,可以用 bytearray:
data = bytearray(b"abc")
data[0] = 65
print(data) # bytearray(b'Abc')
它适合处理需要原地修改的二进制缓冲区。不过普通业务文本处理很少需要它。比如网络协议解析、图片头部修改、二进制包拼装,bytearray 可以避免每改一次都创建新的 bytes。修改值也必须在 0 到 255 之间,否则不是合法字节。
buf = bytearray(b"abc")
buf[1] = 90
print(bytes(buf)) # b'aZc'
易错点:
bytearray可变的是字节内容,不是文本字符;往里面写入的是 0-255 的整数或字节片段,不会自动理解中文字符。
五、encode 和 decode 怎么记
从文本到字节:
text = "你好"
data = text.encode("utf-8")
从字节到文本:
text = data.decode("utf-8")
可以这样理解:
str --encode--> bytes
bytes --decode--> str
编码和解码必须使用匹配的字符集。用 UTF-8 编码的数据,如果用错误编码解码,可能报错,也可能变成乱码。
错误处理参数也值得知道,但不要滥用。errors="ignore" 会丢字节,errors="replace" 会替换成占位字符,适合日志兜底,不适合核心业务数据。面试里说“乱码”时要讲清楚本质:编码和解码规则不一致,或者原始字节本身不符合指定编码。
data = "你好".encode("utf-8")
# data.decode("ascii") # UnicodeDecodeError
print(data.decode("utf-8")) # 你好
六、文件和网络边界怎么选类型
文本模式:
with open("a.txt", "r", encoding="utf-8") as f:
text = f.read()
读出来是 str。
二进制模式:
with open("a.png", "rb") as f:
data = f.read()
读出来是 bytes。
处理文本文件时明确写 encoding="utf-8" 是好习惯,能减少不同系统默认编码导致的问题。
网络 socket、HTTP body、图片、压缩包、哈希输入这些场景通常处理 bytes;业务层展示、日志、JSON 字段通常处理 str。边界处要明确转换,不要让默认编码替你做决定。尤其是 Windows 和 Linux 默认编码可能不同,没写 encoding 的文本文件代码容易在换环境时出问题。
七、常见误区与追问
- 误区:
str就是 UTF-8 字节串。 Python 3 的str是 Unicode 文本,UTF-8 是把它编码成 bytes 的一种规则。 - 误区:
len("你")应该等于 3。len(str)数字符数,len(bytes)数字节数,"你"在 UTF-8 下才是 3 个字节。 - 误区:
bytes索引会返回一个 bytes。b"abc"[0]返回整数 97,切片b"abc"[:1]才返回b"a"。 - 追问:为什么
str和bytes不能直接拼接? 一个是文本层,一个是字节层,Python 要求你显式 encode 或 decode,避免隐式乱码。 - 追问:什么时候用
bytearray? 需要原地修改二进制缓冲区时用;普通业务文本处理通常不用。 - 追问:文件打开为什么建议写
encoding="utf-8"? 避免依赖系统默认编码,降低跨平台乱码风险。
八、加强记忆
把这题记成“文本走 str,传输和存储边界走 bytes,要原地改字节用 bytearray”。encode 是文本到字节,decode 是字节回文本;字符数和字节数不是一回事,编码不匹配才是乱码根源。面试回答时用 "你" 在 UTF-8 下 1 个字符、3 个字节这个例子,基本就能把核心讲清。