Python 文档最后对 return 的解释具有误导性

Python docs have misleading explanation of return in finally

我正在阅读 python 文档来改进我的核心 python 我正在阅读 errors and exceptions

文档中说

If a finally clause includes a return statement, the finally clause’s return statement will execute before, and instead of, the return statement in a try clause.

下面还提供了这个例子:

def bool_return():
    try:
        return True
    finally:
        return False

bool_return()

现在看这个例子,上面的陈述看起来很直接和公平,但是如果你稍微修改那个例子让它看起来像这样:

def bool_return():
    try:
        return print("foo")
    finally:
        return False

bool_return()

现在,如果您 运行 这样做,您将看到 foo 将被打印,而 False 将被 return 编辑。现在文档说 finally 子句的 return 将执行 before,而 而不是 try 子句的 return 语句。如果是这样,那为什么我可以看到正在打印的 foo?

我用 pycharm 调试了这段代码,它显示首先执行 try 子句的 return 语句并打印字符串,然后它的输出 None 是 returned由于return语句,finally子句中的return语句会在后面执行,也就是程序的最后一个return所以函数会覆盖前面的return 和 False 是 returned.

我的问题是:

1) 为什么文档说 finally 子句的 return 语句在 之前 执行?

2) 为什么 doc 说 finally 子句的 return 语句被执行 而不是 try 子句的 return 语句?

我相信这两种说法都与现实情况相反。

编辑:

阅读@iBug 的回答后,现在很清楚 print("foo") 是如何计算的,但 None 不是 return。基本上,首先计算表达式,然后 return 发生。稍后 return False in finally 被执行。这清楚地说明了为什么我们得到了我们所做的输出。

不过,我看到 finally 中的 return False 是在after the return print("foo") of try.

之后执行的

或者按照@iBug 的评论,10 RETURN_VALUE 被完全绕过了?

编辑

这已在文档中得到解决,现在 returned 的内容是正确的。但是,如果你想知道 "how" 请阅读所有评论并仔细回答。

$ python3
Python 3.7.5 (default, Nov 20 2019, 09:21:52)
[GCC 9.2.1 20191008] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def bool_return():
...     try:
...         return print("foo")
...     finally:
...         return False
...
>>> import dis
>>> dis.dis(bool_return)
  2           0 SETUP_FINALLY            8 (to 10)

  3           2 LOAD_GLOBAL              0 (print)
              4 LOAD_CONST               1 ('foo')
              6 CALL_FUNCTION            1
              8 RETURN_VALUE

  5     >>   10 LOAD_CONST               2 (False)
             12 RETURN_VALUE
>>>

正如您在上面看到的,return False 是否发生在 try 块中的 return 语句之前,但在 to- be-returned 值已经计算出来。

我认为文档可能意味着 "the very action of returning" 通过 return 语句 ,或者换句话说,它没有考虑到return 值,这当然发生在 returned 之前。


观察8 RETURN_VALUE是否执行,可以在调试模式下编译CPython解释器,在GDB中运行编译。一步一步的指南对于这个答案来说太臃肿了,所以我会在这里给出一个大纲 (Linux)。

  • 从官方来源(python.org 网站或GitHub)获取 CPython 源代码
  • 配置调试版本 ./configure --with-pydebug(您可能还想提供 --prefix=/opt/python3-debug)、makemake install
  • 在 GDB 中启动调试 Python:gdb /opt/python3-debug/bin/python3 和 (gdb) r
  • 照常定义函数 bool_return
  • Python/ceval.c中找到字符串RETURN_VALUE,记下行号(for 3.8.1, it's 1911)。
  • 通过发送SIGTRAP挂起Python解释器,并在上一步(b Python/ceval.c:1911)的位置设置断点,然后c.
  • 观察两次到达断点,输出如下:
(gdb breakpoint info)
False
(gdb breakpoint info)
  • 观察您在 REPL 中输入的每个语句如何到达断点一次。这是了解到上面步骤中的第二个断点是由Python REPL引起的,所以只有第一个断点来自于函数中的一个return语句。

现在很明显函数里只执行了一条return,肯定是12 RETURN_VALUE,所以Python指令8 RETURN_VALUE没有执行完全没有。