python的print()函数到底做了什么?

8 投票
4 回答
15384 浏览
提问于 2025-04-15 17:28

我在看这个问题的时候,开始好奇print到底是干嘛的。

我一直没搞明白怎么用string.decode()string.encode()在Python的交互式命令行里输出一个和print一样格式的Unicode字符串。不管我怎么尝试,要么就出现

  1. 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 个回答

2
>>> import sys
>>> a = '\xAA\xBB\xCC'
>>> print(a)
ª»Ì

这里的 print 只是把原始的字节写入到 sys.stdout。字符串 a 是一串字节,不是 Unicode 字符。

我为什么要问这个?我对 encode() 出错感到厌烦,意识到因为 print 可以做到这一点(至少在交互式命令行中)。我知道一定有办法可以神奇地正确处理编码,从某个地方获取使用的编码信息...

可惜的是,print 在这里并没有做什么神奇的事情。你给它一些字节,它就把这些字节输出到标准输出。

要正确使用 .encode().decode(),你需要理解字节和字符之间的区别,而且恐怕你得弄清楚该使用什么正确的编码。

5

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)) 就可以解决问题。

9

编辑:(这次编辑和之前的版本有很大变化……注意:我在一台运行Ubuntu的机器上使用Python 2.6.4。)

首先,在我第一次尝试回答时,我提供了一些关于printstr的一般信息,我会把这些信息留在下面,以便那些在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;否则我想我就没有有用的评论了。

撰写回答