核心思想:一句话总结
- Python 2:
str是字节串,unicode是真正的 Unicode 字符串,你需要手动在它们之间进行转换。 - Python 3:
str是真正的 Unicode 字符串,bytes是字节串,转换是自动的,但你需要明确地使用.encode()和.decode()。
Python 2 中的 str 和 unicode
在 Python 2 中,这两种类型是明确分开的,这也是很多初学者和从 Python 2 迁移到 Python 3 的开发者感到困惑的地方。

str 类型:字节串
- 本质:
str类型实际上是一个字节序列,它本身不关心这些字节代表的是哪个字符的编码,它只是原始的、未经处理的字节数组。 - 编码:当你创建一个
str字面量时(s = "你好"),Python 会根据你的源文件编码声明(通常是文件顶部的# -*- coding: utf-8 -*-)或者系统的默认编码,将这些字符编码成特定的字节序列(如 UTF-8, GBK 等)。 - 示例:
# 假设文件编码是 UTF-8 s = "你好" print type(s) # <type 'str'> print repr(s) # u'\u4f60\u597d' (在内部,它被解释为 unicode,但存储为字节) # 更准确的查看方式是: # print [ord(c) for c in s] # 这可能会因为编码不同而得到错误的结果 # 一个更清晰的例子: s_utf8 = "你好" # 在内存中,它存储的是 UTF-8 编码的字节: \xe4\xbd\xa0\xe5\xa5\xbd
unicode 类型:Unicode 字符串
- 本质:
unicode类型才是 Python 中真正意义上的字符串,它存储的是字符的码点,而不是字节,码点是每个字符在 Unicode 字符集中的唯一编号('A' 的码点是 U+0041,'中' 的码点是 U+4E2D)。 - 字面量:在 Python 2 中,
unicode字面量需要在字符串前面加上u前缀,如u"你好"。 - 优势:
unicode对象是“编码无关”的,你可以在其中安全地操作任何语言的字符,而不用担心底层的字节表示。
Python 2 中的转换:encode() 和 decode()
这是 Python 2 中最关键的操作。
-
unicode->str(编码 - Encode):将一个 Unicode 字符串转换成特定编码的字节串。- 方法:
unicode_string.encode(encoding) - 示例:
u_str = u"你好" str_utf8 = u_str.encode('utf-8') print type(str_utf8) # <type 'str'> print repr(str_utf8) # '\xe4\xbd\xa0\xe5\xa5\xbd' (UTF-8 编码的字节)
- 方法:
-
str->unicode(解码 - Decode):将一个字节串(str)根据指定的编码解码成 Unicode 字符串。- 方法:
str_string.decode(encoding) - 示例:
str_utf8 = '\xe4\xbd\xa0\xe5\xa5\xbd' # 这是一个 UTF-8 编码的字节串 u_str = str_utf8.decode('utf-8') print type(u_str) # <type 'unicode'> print repr(u_str) # u'\u4f60\u597d' (真正的 Unicode 字符串)
- 方法:
Python 2 的常见问题:UnicodeDecodeError 和 UnicodeEncodeError
如果你不明确地进行转换,Python 2 会尝试使用默认的 ASCII 编码进行转换,这通常会导致错误。
-
UnicodeDecodeError:当你尝试将一个str(字节串)当作文本处理,但没有指定正确的编码时发生。# str_utf8 是一个 UTF-8 字节串 str_utf8 = '\xe4\xbd\xa0\xe5\xa5\xbd' # Python 2 默认尝试用 ASCII 解码 print str_utf8.decode('ascii') # UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128) -
UnicodeEncodeError:当你尝试将一个unicode字符串写入文件或打印到终端,但终端或文件的编码不支持该字符时发生。# u_str 是一个包含中文字符的 unicode 字符串 u_str = u"你好" # 尝试用 ASCII 编码成字节串 print u_str.encode('ascii') # UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
最佳实践(Python 2):
- 内部处理全部使用
unicode,在你的程序逻辑中,所有文本数据都应该是unicode类型。 - 只在输入/输出时进行编码/解码,当从文件、网络、数据库或用户输入读取数据时,立即用正确的编码
decode成unicode,当需要写入或发送数据时,在最后一步用合适的编码encode成str。
Python 3 中的 str 和 bytes
Python 3 彻底解决了 Python 2 的混乱局面,让字符串模型更加清晰和直观。
str 类型:Unicode 字符串
- 本质:在 Python 3 中,
str类型就是真正的 Unicode 字符串,它的行为和 Python 2 中的unicode类型完全一样,它存储的是字符的码点,而不是字节。 - 字面量:普通的字符串字面量()
str类型。 - 示例:
s = "你好" print(type(s)) # <class 'str'> print(repr(s)) # '你好' (直接显示字符,而不是字节码)
bytes 类型:字节串
-
本质:
bytes类型是不可变的字节序列,它的行为和 Python 2 中的str类型非常相似。 -
字面量:
bytes字面量需要在前面加上b前缀,如b"hello"。 -
特点:
- 它只能包含 ASCII 范围内的字符(0-127),对于非 ASCII 字符,你需要使用转义序列。
bytes对象上的方法都是针对字节的操作(如find,replace,split),而不是针对字符。
-
示例:
b_str = b"hello" print(type(b_str)) # <class 'bytes'> print(repr(b_str)) # b'hello' # 对于非 ASCII 字符,必须使用转义 b_str_chinese = b'\xe4\xbd\xa0\xe5\xa5\xbd' # 这是 UTF-8 编码的字节 print(repr(b_str_chinese)) # b'\xe4\xbd\xa0\xe5\xa5\xbd' # 下面的代码会报错,因为 b"你好" 不是有效的字节字面量 # b_str_invalid = b"你好" # SyntaxError: bytes can only contain ASCII literal characters.
Python 3 中的转换:.encode() 和 .decode()
转换的概念和 Python 2 一样,但语法更清晰,并且是 str 和 bytes 之间唯一的转换方式。
-
str->bytes(编码 - Encode):将 Unicode 字符串编码成字节串。- 方法:
string.encode(encoding) - 示例:
s = "你好" # 这是一个 str (unicode) b_utf8 = s.encode('utf-8') print(type(b_utf8)) # <class 'bytes'> print(repr(b_utf8)) # b'\xe4\xbd\xa0\xe5\xa5\xbd'
- 方法:
-
bytes->str(解码 - Decode):将字节串解码成 Unicode 字符串。- 方法:
bytes_object.decode(encoding) - 示例:
b_utf8 = b'\xe4\xbd\xa0\xe5\xa5\xbd' # 这是一个 bytes s = b_utf8.decode('utf-8') print(type(s)) # <class 'str'> print(repr(s)) # '你好'
- 方法:
Python 3 的优势:
- 清晰性:
str就是文本,bytes就是二进制数据,类型本身的名字就说明了它的用途,消除了 Python 2 中的str和unicode的混淆。 - 默认安全性:Python 3 在大多数情况下会强制你进行编码和解码,避免了隐式的、错误的 ASCII 转换。
# Python 3 s = "你好" # 尝试将 str 和 bytes 连接 # s + b" world" # TypeError: can only concatenate str (not "bytes") to str
这个错误提示非常明确,告诉你不能直接拼接文本和二进制数据,你必须先统一类型。
总结与对比
| 特性 | Python 2 | Python 3 |
|---|---|---|
| 文本字符串 | unicode |
str |
| 字节串 | str |
bytes |
| 字面量 | u"你好" |
"你好" |
| 字节字面量 | "hello" (编码后) |
b"hello" |
| 编码 | u_str.encode('utf-8') -> str |
str.encode('utf-8') -> bytes |
| 解码 | str_str.decode('utf-8') -> unicode |
bytes_obj.decode('utf-8') -> str |
| 核心问题 | str 和 unicode 混淆,易出错 |
类型清晰,强制显式转换,更安全 |
| 哲学 | "文本和字节可以自动转换"(但常常出错) | "文本和字节是不同的东西,必须显式转换" |
迁移建议
如果你正在将 Python 2 代码迁移到 Python 3,最核心的任务就是处理所有的字符串编码问题:
- 识别所有
str对象:判断它们是真正的文本(应该变成str)还是原始字节(应该变成bytes)。 - 添加编码声明:确保你的 Python 2 源文件顶部有
# -*- coding: utf-8 -*-。 - 使用
2to3工具:这个工具可以自动处理大部分的语法转换,包括unicode字面量变成str。 - 处理 I/O:在 Python 3 中,文件操作默认使用系统编码,对于文本文件,最好在打开时明确指定编码:
# Python 3 with open('file.txt', 'r', encoding='utf-8') as f: content = f.read() # content 是 str对于二进制文件,使用
'rb'或'wb'模式:# Python 3 with open('file.dat', 'wb') as f: f.write(b'\x00\x01\x02') # 必须写入 bytes
希望这个详细的解释能帮助你彻底理解 Python 2 和 Python 3 中的字符串处理!
