我正在阅读 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 False
在 return print("foo")
之后 终于执行了的尝试。
或者根据@iBug 的评论,10 RETURN_VALUE
被完全绕过?
编辑
这个问题现在在文档中得到了解决,现在返回的内容是正确的。但是,如果您想知道“如何”,请阅读所有评论并仔细回答。
最佳答案
$ 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
),make
和make 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/