核心思想:Python 2 的“默认混乱” vs Python 3 的“显式Unicode”
- Python 2: 将字符串默认视为字节的序列,这导致了大量的混乱,开发者需要手动处理“字节串”和“Unicode字符串”之间的转换。
- Python 3: 将字符串默认视为 Unicode 字符的序列,这使得处理国际化文本变得简单和直观,字节串和字符串是两种完全不同的类型。
Python 2 的编码世界
在 Python 2 中,有两种主要的字符串类型:

str: 字节串,它是一堆字节的序列,本身不包含任何编码信息,它的行为更像是 C 语言中的char *。unicode: Unicode 字符串,它是一个抽象的字符序列,可以表示世界上几乎所有的字符。
问题根源:默认编码是 ASCII
当你写下 s = "你好" 时,Python 2 默认会使用系统的 ASCII 编码来尝试转换这些字符,因为 "你好" 不在 ASCII 字符集内,所以会抛出 SyntaxError。
解决方案:你必须在字符串前加上 u 前缀,明确声明这是一个 Unicode 字符串。
# 正确写法 s_unicode = u"你好" print type(s_unicode) # <type 'unicode'> # 错误写法 (在非 ASCII 环境下) # s_str = "你好" # SyntaxError: Non-ASCII character '\xe4' in file ...
str 和 unicode 的转换
当你需要将 Unicode 字符串写入文件、网络发送或与某些 C 库交互时,必须将其编码成字节串 (str),反之,从字节串读取时,需要解码成 Unicode 字符串。
- 编码:
unicode_string.encode(encoding) - 解码:
byte_string.decode(encoding)
示例:

# 1. 定义一个 Unicode 字符串
u_str = u"你好,世界!"
# 2. 将 Unicode 字符串编码成 UTF-8 格式的字节串
utf8_bytes = u_str.encode('utf-8')
print type(utf8_bytes) # <type 'str'>
print repr(utf8_bytes) # '\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c\xef\xbc\x81'
# 3. 将字节串解码回 Unicode 字符串
decoded_u_str = utf8_bytes.decode('utf-8')
print type(decoded_u_str) # <type 'unicode'>
print decoded_u_str # 你好,世界!
Python 2 的“隐式转换”陷阱
Python 2 在某些情况下会尝试在 str 和 unicode 之间进行隐式转换,这通常发生在拼接操作时,如果一方是 str,另一方是 unicode,Python 会尝试将 str 解码成 unicode(使用默认的 ASCII 编码),这常常导致 UnicodeDecodeError。
s_str = "hello" # 这是一个 str (字节串)
u_str = u"你好" # 这是一个 unicode
# 尝试拼接
try:
result = s_str + u_str
except UnicodeDecodeError as e:
print "错误:", e
# 错误: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
正确做法:总是显式地进行编码和解码。
# 正确做法
result = s_str.decode('utf-8') + u_str
# 或者
result = s_str + u_str.encode('utf-8')
Python 3 的编码世界
Python 3 对字符串模型进行了彻底的改革,消除了 Python 2 的混乱。
str: Unicode 字符串,这是处理文本的默认类型,它的行为和 Python 2 的unicode一样。bytes: 字节串,这是一个只包含字节的序列,和 Python 2 的str类似。
核心思想:str 就是文本,bytes 就是数据
在 Python 3 中,"你好" 直接就是一个 Unicode 字符串,不需要 u 前缀了。

# Python 3 s = "你好" print(type(s)) # <class 'str'>
str 和 bytes 的转换
Python 3 不再有隐式转换,当你需要将文本(str)和二进制数据(bytes)相互转换时,必须明确地使用 .encode() 和 .decode() 方法。
- 编码:
text_string.encode(encoding) - 解码:
byte_string.decode(encoding)
示例:
# 1. 定义一个 str (Unicode 字符串)
text = "你好,世界!"
# 2. 将 str 编码成 bytes
# .encode() 方法返回一个 bytes 对象
byte_data = text.encode('utf-8')
print(type(byte_data)) # <class 'bytes'>
print(repr(byte_data)) # b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c\xef\xbc\x81'
# 3. 将 bytes 解码回 str
# .decode() 方法返回一个 str 对象
decoded_text = byte_data.decode('utf-8')
print(type(decoded_text)) # <class 'str'>
print(decoded_text) # 你好,世界!
Python 3 的严格性
Python 3 强制你区分文本和二进制数据。open() 函数默认以文本模式('r')打开文件,返回的是 str,如果你想以二进制模式('rb')打开文件,返回的是 bytes。
# 文本模式 (默认)
with open('my_file.txt', 'r', encoding='utf-8') as f:
content = f.read() # content 是 str
print(type(content))
# 二进制模式
with open('my_file.txt', 'rb') as f:
content = f.read() # content 是 bytes
print(type(content))
总结与对比
| 特性 | Python 2 | Python 3 | 说明 |
|---|---|---|---|
| 默认字符串类型 | str (字节串) |
str (Unicode字符串) |
Python 3 的 str 相当于 Python 2 的 unicode。 |
| Unicode字符串 | u"..." |
Python 3 中 u 前缀是可选的,推荐省略。 |
|
| 字节串类型 | str |
bytes |
Python 3 明确区分 str 和 bytes。 |
| 文件I/O | open() 返回 str,需要手动编码/解码。 |
open() 默认文本模式,返回 str;'rb' 模式返回 bytes。 |
Python 3 的文件I/O更清晰,encoding参数是文本模式的必需项。 |
| 隐式转换 | 存在,容易导致 UnicodeDecodeError。 |
不存在,str 和 bytes 不能直接拼接,强制显式转换。 |
Python 3 的严格性避免了大量编码错误。 |
| 编码方法 | unicode_str.encode() |
str.encode() |
功能相同,但调用对象类型不同。 |
| 解码方法 | byte_str.decode() |
bytes.decode() |
功能相同,但调用对象类型不同。 |
| 打印 | print "hello" (语句) |
print("hello") (函数) |
虽然不是编码问题,但这是最基础的语法区别。 |
最佳实践(如何写出健壮的代码)
Python 2 兼容代码
- 永远使用
u前缀:在处理任何非 ASCII 文本时,都使用u"..."来定义 Unicode 字符串。 - 在文件I/O时明确编码:始终在打开文件时指定编码,通常是
'utf-8'。# Python 2 with open('file.txt', 'r') as f: # 假设文件是 UTF-8 编码 content = f.read().decode('utf-8') - 统一内部编码:将应用程序的内部编码统一为 Unicode,只在需要与外部世界(文件、网络、控制台)交互时,才进行编码和解码。
- 使用
from __future__ import unicode_literals:这会使 Python 2 将所有不带u前缀的字符串字面量都当作 Unicode 字符串处理,减少迁移到 Python 3 的工作量。
Python 3 代码
-
拥抱
str:放心地使用 来表示所有文本。 -
显式处理二进制数据:当你处理文件、网络数据或图像时,明确使用
bytes类型。 -
始终指定文件编码:
# Python 3 with open('file.txt', 'r', encoding='utf-8') as f: content = f.read() # content 直接是 str -
不要混合
str和bytes:如果需要拼接,先统一类型。# Python 3 text = "hello" binary_data = b" world" # 错误: TypeError: can only concatenate str (not "bytes") to str # result = text + binary_data # 正确 result = text + binary_data.decode('utf-8') # 或者 result = text.encode('utf-8') + binary_data
Python 3 的编码模型是 Python 2 的巨大进步,它通过将“文本”(str)和“二进制数据”(bytes)明确分离,并强制开发者进行显式的编码和解码,从根本上解决了 Python 2 中令人头疼的 Unicode 问题,虽然对于从 Python 2 迁移过来的开发者来说需要改变一些习惯,但一旦适应,代码会变得更加健壮和易于维护。
