结论:str 是 Unicode 码点序列(文本),bytes 是字节序列(原始数据),编码(encode)把 str 变 bytes,解码(decode)反之。Python 3 的铁律是两者不隐式转换——'a' + b'b' 直接 TypeError,边界(文件、网络、进程 IO)处必须显式指定编码。
展开:最佳实践是"Unicode 三明治":进入系统立刻 decode 成 str,内部全部用 str 处理,离开系统才 encode。UnicodeDecodeError 的排查套路:1)确认真实编码——文件头部、协议声明、chardet 探测,不要默认猜 UTF-8(中文 Windows 文件常是 GBK);2)定位边界——open() 不传 encoding 会用平台默认编码(locale 决定),这是"本地正常、服务器炸"的头号原因,应永远显式写 encoding='utf-8';3)决定容错策略——errors='replace' 用 占位、'ignore' 丢弃、'strict' 抛错,日志清洗场景用 replace 保流程。易错点:str 的长度按码点算,emoji 和组合字符的"用户感知字符"(grapheme cluster)可能跨多个码点;bytes 迭代得到 int 不是单字节 bytes。
with open(path, encoding='utf-8', errors='replace') as f:
text = f.read()
data = text.encode('utf-8') # str → bytes
assert data.decode('utf-8') == text # 往返
追问方向:UTF-8 的变长编码设计为何兼容 ASCII、surrogate 与 sys.getfilesystemencoding() 的关系。