Python 文档对 finally 中的 return 有误导性的解释

标签 python python-3.x try-except 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。现在文档说 finally 子句的返回将执行 before而不是 try 子句的 return 语句。如果是这样,那为什么我可以看到正在打印的 foo?

我用 pycharm 调试了这段代码,它显示首先执行 try 子句的 return 语句并打印字符串,然后输出 None由于 return 被退回语句,finally 子句中的 return 语句将在后面执行,这是程序的最后一次返回,因此函数覆盖了之前的 return 和 False被退回。

我的问题是:

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

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

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

编辑:

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

我仍然看到 return Falsereturn print("foo") 之后 终于执行了的尝试。

或者根据@iBug 的评论,10 RETURN_VALUE被完全绕过?

enter image description here

编辑

这个问题现在在文档中得到了解决,现在返回的内容是正确的。但是,如果您想知道“如何”,请阅读所有评论并仔细回答。

最佳答案

$ 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 block 中的 return 语句之前,但是在计算出要返回的值之后。

我认为文档可能意味着 return 语句 的“返回的 Action ”,或者换句话说,它没有考虑返回值的计算,这当然会发生在返回之前。


要观察8 RETURN_VALUE是否执行,可以在 Debug模式下编译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 根本不执行。

关于Python 文档对 finally 中的 return 有误导性的解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59639733/

相关文章:

python - 当我在柴油中使用产量时,telnet 连接关闭

python - 在 Django 中访问 BASE_DIR 的推荐方式

python - 尝试除了 float 但不是整数

python - 如何检查用户输入是否是字符串

Python,尝试异常(exception),打印错误行#

python - 无法点击元素 : ElementClickInterceptedException in Splinter/Selenium

Python:用土耳其语字母阅读和打印土耳其语的问题

python - 在python opencv中获取强度为255的二进制图像的像素位置

python - 如何获取导致 any() 返回 True 的值?

python - 在 django 中处理零行时 filter().update() 的最佳方法?