【问题标题】:Why do I get extra output from code using 'print' (or output at all, using 'return') at the REPL, but not in a script?为什么我在 REPL 中使用 \'print\' 而不是在脚本中从代码中获得额外的输出?
【发布时间】:2022-08-16 12:36:21
【问题描述】:

假设我制作了一个简单的脚本hello.py,其中仅包含:

print(\'hello world\')

当我运行它时,我只在输出中看到hello world。如果我在解释器提示 (REPL) 中尝试相同的代码,我会看到相同的结果:

>>> print(\'hello world\')
hello world

但是,如果我尝试更复杂的事情,例如:

print(\'hello\'), print(\'world\')

当我运行脚本时,我将每个单词放在单独的行上,正如我所期望的那样。但在 REPL:

>>> print(\'hello\'), print(\'world\')
hello
world
(None, None)

额外的(None, None) 输出是什么?为什么在第一个示例中没有任何额外的输出?

    标签: python python-3.x


    【解决方案1】:

    概括

    print 在第一种情况下产生 hello world 输出,无论是来自 REPL 还是脚本;以及第二种情况下单独的helloworld 行,同样来自REPL 或脚本。REPL 本身产生(None, None) 输出,因为那是评估结果print('hello'), print('world')。仅评估 print('hello world') 的结果是 None,但 REPL隐藏None 结果作为特例.

    将文本写入程序的标准输出流时会显示文本。评估表达式,从函数返回等。不会自动执行此操作. print 确实显示输出(这是它的目的),并且 REPL(在您的程序之外)也显示(除了当结果为None)。

    这就是你真正需要知道的一切;但这里有一些关于整个系统如何工作的细节。

    print 的工作原理

    在 3.x 中,print 是一个函数。当您调用它时,它会返回特殊值None

    “什么是None?”见What is a 'NoneType' object?

    “为什么它会返回那个?”因为在 Python 中,对函数的调用是表达;它has to return something1.如果一个函数在没有显式返回的情况下到达末尾,None 就是你隐式得到的。

    “所以它不返回格式化文本?”不,为什么会这样? Python 风格适用于do something as a side effect or return something other than None, not both 的函数。

    “等等,但是它怎么能显示任何东西呢?”它不需要return 任何东西都是为了显示文本,实际上是return has nothing to do with displaying anything。在命令行运行的程序通过将输出写入标准输出流, 这有点像操作系统提供的特殊文件。2

    REPL,以及它如何处理代码

    当您启动 Python 而不给它运行脚本或模块时,您会得到如下所示的内容:

    Python 3.8.10 (default, Jun 22 2022, 20:18:18) 
    [GCC 9.4.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>>
    

    (当然,细节取决于你的操作系统、Python 版本以及它是如何编译的。)

    tutorial in the official documentation 将 Python 可执行文件本身称为“解释器”,并表示此处我们已“以交互模式”启动它。在更通俗的语言中,Python 社区通常将这种模式和>>> 提示符称为“REPL”。3

    Python 中的代码块(例如,您可能以ifdef 开头,然后是一些缩进的行)不会评估任何东西——甚至None。他们是陈述,而不是表达式。4但是调用函数是表达,因此它评估为可以显示的结果。当您输入一个表达式时,解释器会显示一个文本的代表结果是from repr5

    除非结果是None. Nonerepr,正如您可能已经猜到的那样,是 None(即文字,不带引号)。看到每次调用printuse .extend on a list 等时都会让人分心。所以解释器对此进行了特殊处理,并隐藏了None 结果。

    print('hello'), print('world') 是来自 print 调用的两个 None 值中的一个 expression that makes a tuple。所以结果就是:(None, None)。这不是None 的特例,所以会显示出来。6

    代码在脚本中的行为方式

    当您从 .py 文件运行 Python 代码时,不再有 REPL。因此,尽管代码不断地评估表达式,但没有任何结果可显示 - 除非您使用 print 明确执行此操作。例如,您可以创建一个仅包含1 + 1 的测试.py 文件并运行它;这是完全有效的 Python,它会计算2 的总和 - 它不会展示任何事物。同样,示例代码 print('hello'), print('world') 计算两个 None 值的元组,但是不显示.7


    1当然,或者它可能会引发异常。

    2在 Python 中,可以从sys 标准库模块以stdout 访问标准输出流(因此,from sys import stdout 然后使用stdout,或者import sys 然后使用sys.stdout)。 Python 将它表示为一个为写入文本而打开的文件。同样,还有一个标准错误流,可作为sys.stderr 访问,用于编写错误消息。有单独的流,因此命令行程序可以在它们相互调用并解释彼此的输出时将这两个信息流分开。这种设计已有数十年的历史。无论如何,实际上更改终端窗口中的像素颜色已经完成通过终端程序,而不是 Python。 Python 只是说明要显示的文本。

    3这代表“Read-Eval-Print Loop”,在许多编程语言中都可以看到an established concept。它的意思就是它听起来的样子:当你在 REPL 提供代码时,解释器那个代码,评估评价它,并且可能打印s(显示)结果。它一直在这样做,在环形, 直到您退出命令行。

    4作业也是如此,这就是为什么你不能做x = (y = 1)(x = y) = 1即使你可以x = y = 1

    5它实际上不能显示整数;它显示代表的文字以十为底的整数。整数没有十进制(或二进制,或十六进制......)数字。只有那些表示可以。为了清楚地思考问题,程序员做出这些区分是非常重要的。

    6在 2.x 中,print 是一个语句(最新版本除外,如果您启用了向前兼容性选项)。所以 REPL 没有 None 可以压制,而且,像 print('hello'), print('world') 这样的代码无论如何也不可能出现。

    7我经常看到代码——尤其是在 Pandas 示例中——有人似乎已经从交互式会话中复制并粘贴到源文件中(在弄清楚什么是有效的之后),留下只有变量名的行(因为在测试它时在 REPL 中,作者决定在那时检查变量的值)。在脚本中,这是良性的,但也是无用的——最好去掉这样的行。

    我还经常看到使用列表推导替换仅用于其副作用的 for 循环的代码。 Please don't do that。它不是有害使用列表推导创建列表,但它是无用的(你最终得到一个 None 值列表,你不用于任何事情),不直观(列表推导用于创建列表;循环用于重复一个过程) 并且效率可能会稍低一些。

    【讨论】:

      猜你喜欢
      • 2015-01-09
      • 2022-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-24
      • 2021-04-02
      • 1970-01-01
      相关资源
      最近更新 更多