python的print()函数到底做了什么?
我在看这个问题的时候,开始好奇print
到底是干嘛的。
我一直没搞明白怎么用string.decode()
和string.encode()
在Python的交互式命令行里输出一个和print
一样格式的Unicode字符串。不管我怎么尝试,要么就出现
- UnicodeEncodeError错误,要么就得到带有"\x##"格式的转义字符串...
这是在用Python 2.x,不过我已经在努力改正,开始用print()
了 :)
举个例子:
>>> import sys
>>> a = '\xAA\xBB\xCC'
>>> print(a)
ª»Ì
>>> a.encode(sys.stdout.encoding)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
UnicodeDecodeError: 'ascii' codec can't decode byte 0xaa in position 0: ordinal not in range(128)
>>> a.decode(sys.stdout.encoding)
u'\xaa\xbb\xcc'
编辑:
我为什么会问这个呢?我真是受够了encode()
的错误,意识到既然print
可以做到(至少在交互式命令行里可以)。我知道一定有办法能神奇地正确编码,得找个地方挖掘一下该用什么编码...
附加信息: 我在linux2上运行的是Python 2.4.3(#1, 2009年9月3日, 15:37:12)[GCC 4.1.2 20080704 (Red Hat 4.1.2-46)]
>>> sys.stdin.encoding
'ISO-8859-1'
>>> sys.stdout.encoding
'ISO-8859-1'
不过,在同一台Linux机器上,Python 2.6.2(r262:71600, 2009年9月8日, 13:06:43)的结果也是一样的。
4 个回答
>>> import sys
>>> a = '\xAA\xBB\xCC'
>>> print(a)
ª»Ì
这里的 print
只是把原始的字节写入到 sys.stdout
。字符串 a
是一串字节,不是 Unicode 字符。
我为什么要问这个?我对
encode()
出错感到厌烦,意识到因为
可惜的是,print
在这里并没有做什么神奇的事情。你给它一些字节,它就把这些字节输出到标准输出。
要正确使用 .encode()
和 .decode()
,你需要理解字节和字符之间的区别,而且恐怕你得弄清楚该使用什么正确的编码。
print()
函数会使用 sys.stdout.encoding
来判断输出控制台能理解什么样的编码,然后在调用 str.encode()
时使用这种编码。
[编辑] 如果你查看源代码,它会获取 sys.stdout
,然后调用:
PyFile_WriteObject(PyTuple_GetItem(args, i), file,
Py_PRINT_RAW);
我想这里的关键在于 Py_PRINT_RAW
,但是源代码只是说:
if (flags & Py_PRINT_RAW) {
value = PyObject_Str(v);
}
所以这里没有什么特别的。对参数进行循环,用 sys.stdout.write(str(item))
就可以解决问题。
编辑:(这次编辑和之前的版本有很大变化……注意:我在一台运行Ubuntu的机器上使用Python 2.6.4。)
首先,在我第一次尝试回答时,我提供了一些关于print
和str
的一般信息,我会把这些信息留在下面,以便那些在print
上遇到简单问题的人看到。至于我对提问者遇到的问题的新尝试……基本上,我想说这里没有简单的解决办法,如果print
能够理解一个奇怪的字符串,那也不是一种可以重复的行为。我得出这个结论是因为我在终端窗口与Python的有趣互动:
>>> print '\xaa\xbb\xcc'
��
你有没有尝试直接从终端输入 ª»Ì?在使用utf-8编码的Linux终端中,这实际上会被读取为六个字节,然后可以借助decode
方法将其看作三个unicode字符:
>>> 'ª»Ì'
'\xc2\xaa\xc2\xbb\xc3\x8c'
>>> 'ª»Ì'.decode(sys.stdin.encoding)
u'\xaa\xbb\xcc'
所以,'\xaa\xbb\xcc'
这个字面量只有在作为latin-1字面量解码时才有意义(实际上你可以使用其他与latin-1在相关字符上相同的编码)。至于print
在你的情况下“正常工作”,对我来说可不是这样——如上所述。
这是因为当你使用一个没有u
前缀的字符串字面量时——也就是说,使用"asdf"
而不是u"asdf"
——生成的字符串会使用一些非unicode编码。实际上,字符串对象本身对编码是无感知的,你必须把它当作用编码x编码的字符串,x的正确值是什么就是什么。这一基本概念让我得出以下结论:
a = '\xAA\xBB\xCC'
a.decode('latin1')
# result: u'\xAA\xBB\xCC'
print(a.decode('latin1'))
# output: ª»Ì
注意没有解码错误和正确的输出(我希望在其他机器上也能保持正确)。显然你的字符串字面量可以被Python理解,但这并不是没有帮助的。
这样解释有帮助吗?(至少在理解事情是如何运作的方面,如果不能让处理编码变得更简单……)
现在来一些有趣的内容,希望能有解释价值!这对我来说工作得很好:
sys.stdout.write("\xAA\xBB\xCC".decode('latin1').encode(sys.stdout.encoding))
跳过解码或编码部分会导致与unicode相关的异常。从理论上讲,这很有道理,因为第一次解码是为了确定给定字符串中有哪些字符(乍一看唯一明显的就是有哪些字节——Python 3的想法是用(unicode)字符串表示字符,用字节表示字节,这突然显得非常合理),而编码是为了确保输出符合输出流的编码。现在这个
sys.stdout.write("ąöî\n".decode(sys.stdin.encoding).encode(sys.stdout.encoding))
也按预期工作,但字符实际上是来自键盘,因此实际上是用stdin编码的……另外,
ord('ą'.decode('utf-8').encode('latin2'))
返回正确的177(我的输入编码是utf-8),但'\xc4\x85'.encode('latin2')
对Python来说没有意义,因为它不知道如何理解'\xc4\x85'
,并认为尝试使用'ascii'编码是它能做的最好选择。
原始回答:
Python文档中相关部分(针对版本2.6.4)说print(obj)
的目的是打印出由str(obj)
给出的字符串。我想你可以把它包裹在unicode
的调用中(如unicode(str(obj))
)来得到一个unicode字符串——或者你可以直接使用Python 3,把这个特定的麻烦换成几个不同的麻烦。;-)
顺便提一下,这表明你可以像处理调用str
的结果一样处理print
的结果,也就是通过修改__str__
方法。例子:
class Foo(object):
def __str__(self):
return "I'm a Foo!"
print Foo()
至于print
的实际实现,我预计这不会有任何用处,但如果你真的想知道发生了什么……它在Python源代码的Python/bltinmodule.c
文件中(我查看的是版本2.6.4)。搜索以builtin_print
开头的行。实际上这非常简单,没有什么魔法。:-)
希望这能回答你的问题……但如果你有我完全忽略的更复杂的问题,请评论,我会再试一次。此外,我假设我们在处理Python 2.x;否则我想我就没有有用的评论了。