这里涉及到 Python 功能的多个部分:读取源代码和解析字符串文字、转码和打印。每个部分都有自己的惯例。
简短回答:
- 用于代码解析的目的:
str
(Py2) -- 不适用,从文件中获取原始字节
unicode
(Py2)/str
(Py3) -- "源编码",默认为 ascii
(Py2) 和 utf-8
(Py3)
bytes
(Py3) -- 无,字面值中禁止使用非ASCII字符
- 用于转码的目的:
- both (Py2) --
sys.getdefaultencoding()
(几乎总是ascii
)
- 通常会发生隐式转换,导致
UnicodeDecodeError
/UnicodeEncodeError
- both (Py3) -- 无,必须在转换时明确指定编码
- 用于I/O的目的:
unicode
(Py2) -- 如果设置了,则为<file>.encoding
,否则为sys.getdefaultencoding()
str
(Py2) -- 不适用,写入原始字节
str
(Py3) -- <file>.encoding
,始终设置并默认为locale.getpreferredencoding()
bytes
(Py3) -- 无,print
会产生其repr()
首先,为了让您正确理解后面的内容,我们需要澄清一些术语。
解码 是将
字节 转换成
字符(Unicode 或其他字符集) 的过程,而
编码(作为一个过程)则是相反的过程。请参阅
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) – Joel on Software以获取两者之间的区别。
现在...
读取源代码和解析字符串字面量
在源文件开头,您可以指定文件的“源编码”(其确切影响稍后会描述)。如果未指定,默认值为 Python 2 的 ascii
和 Python 3 的 utf-8
。UTF-8 BOM 具有与 utf-8
编码声明相同的效果。
Python 2
Python 2以原始字节形式读取源代码。只有在遇到Unicode文字时,它才使用“源编码”来解析它们。(在底层实现上比这更复杂,但这是净效应.)
> type t.py
s = "абвгд"
us = u"абвгд"
print repr(s), repr(us)
> py -2 t.py
'\xe0\xe1\xe2\xe3\xe4' u'\u0430\u0431\u0432\u0433\u0434'
<change encoding declaration in the file to cp866, do not change the contents>
> py -2 t.py
'\xe0\xe1\xe2\xe3\xe4' u'\u0440\u0441\u0442\u0443\u0444'
<transcode the file to utf-8, update declaration or replace with BOM>
> py -2 t.py
'\xd0\xb0\xd0\xb1\xd0\xb2\xd0\xb3\xd0\xb4' u'\u0430\u0431\u0432\u0433\u0434'
因此,
普通字符串将包含文件中的确切字节。而
Unicode字符串将包含使用“源编码”解码文件字节的结果。
如果解码失败,则会出现
SyntaxError
。如果文件中有非ASCII字符且未指定编码,则也会出现此情况。最后,如果使用
unicode_literals
future,则在解析时任何常规字符串文字(
仅在该文件中)都被视为Unicode文字,具有所有这意味着什么的内容。
Python 3
Python 3使用“源编码”将整个源文件解码为一系列Unicode字符。然后进行任何解析。(特别是,这使得标识符中可以使用Unicode。)由于所有字符串文字现在都是Unicode,因此不需要进行其他转换。在字节文字中,禁止使用非ASCII字符(这些字节必须用转义序列指定),从而避免了这个问题。
转码
根据开头的澄清:
str
(Python 2)/bytes
(Python 3) -- 字节 => 只能直接进行decode
(详细信息见下)
unicode
(Python 2)/str
(Python 3) -- 字符 => 只能进行encode
Python 2
在这两种情况下,如果未指定编码,则使用
sys.getdefaultencoding()
。它是
ascii
(除非您取消注释site.py
中的代码块,或执行其他黑客行为,这将导致灾难)。因此,
为了进行转码,sys.getdefaultencoding()
是“字符串的默认编码”。
现在,这里有一个警告:
Python 3
现在根本没有“默认编码”:str
和 bytes
之间的隐式转换已被禁止。
bytes
只能被 decode
成 str
-- encode
,且必须指定 encoding
参数。
- 将
bytes->str
(包括隐式转换)转换会产生其 repr()
(仅用于调试打印),完全规避了编码问题。
- 禁止将
str->bytes
转换。
打印
这个问题 与变量的值无关,但与在屏幕上 print
时看到的内容以及是否在 print
时出现 UnicodeEncodeError
相关。
Python 2
- 如果设置了
<file>.encoding
,则unicode
将使用该编码进行encode
;否则,根据上述情况隐式转换为str
。(最后三分之一的UnicodeEncodeError
SO问题属于这里。)
- 对于标准流,流的编码在启动时从各种环境特定源猜测,并可以通过
PYTHONIOENCODING
环境变量进行覆盖。
str
的字节会按原样发送到操作系统流中。您在屏幕上看到的具体字形取决于终端的编码设置(如果是类似UTF-8的东西,则如果打印无效的UTF-8字节序列,则可能什么也看不到)。
Python 3
更改如下: