给定
>>> foo = [1, 1]
>>> bar = [1, 1, 1]
>>> print(foo[5], bar[5])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
如何判断哪个列表引发了异常?
对于更多非琐碎的例子来说,这是非常令人恼火的。有人能解释一下Python解释器是如何工作的,以至于它记不住它所在的字符吗?这似乎是一个太明显的设计错误,所以一定有原因,对吧?
Traceback (most recent call last):
File "C:UsersusernameDocumentsmodule.py", line 3, in <module>
print(foo[5], bar[5])
~~~^^^
IndexError: list index out of range
这要归功于在Python 3.11中实现的PEP 657,目前处于alpha版本,并将于2022年10月发布。
请注意,这个功能附带的内存成本很小,作者已经承认了这一点,他们说:
我们知道,某些用户可能无法接受此信息的额外成本,因此我们提出了一种选择退出机制,该机制将导致生成的代码对象没有额外信息,同时也允许pyc文件不包含额外信息。
可以通过将-Xno_debug_ranges
传递给python
来选择退出此功能。
简单的答案是,您可以通过将打印语句拆分为多行来强制执行(尽管我同意这更多的是一种变通方法,而不是真正的解决方案):
foo = [1, 1]
bar = [2, 2]
print(
foo[5],
bar[5]
)
应该会产生这样的结果:
Traceback (most recent call last):
File "test.py", line 4, in <module>
foo[5],
IndexError: list index out of range
同样需要注意的是,如果想要实际检查字节码以查看发生了什么,dis
模块可能会在这里有所帮助:
import dis
code_block ='''
foo = [1, 1]
bar = [2, 2]
print(foo[5], bar[5])
'''
print(dis.dis(compile(code_block, "", "exec")))
告诉我:
2 0 LOAD_CONST 0 (1)
2 LOAD_CONST 0 (1)
4 BUILD_LIST 2
6 STORE_NAME 0 (foo)
3 8 LOAD_CONST 1 (2)
10 LOAD_CONST 1 (2)
12 BUILD_LIST 2
14 STORE_NAME 1 (bar)
4 16 LOAD_NAME 2 (print)
18 LOAD_NAME 0 (foo)
20 LOAD_CONST 2 (5)
22 BINARY_SUBSCR
24 LOAD_NAME 1 (bar)
26 LOAD_CONST 2 (5)
28 BINARY_SUBSCR
30 CALL_FUNCTION 2
32 POP_TOP
34 LOAD_CONST 3 (None)
36 RETURN_VALUE
None
因此,在我的平台上,foo[5]
在bar之前进行评估(通过BINARY_SUBSCR.)。
I在返回时使用反编译编写了替换,以显示回溯中的位置。请参见loctraceback。它没有得到太多关注,我已经有一段时间没有尝试使用它了。
trepan3k调试器还使用反编译来更准确地显示停止时的位置。
2018年,我就这里使用的技术做了一次简短的演讲。有趣的是,当我举这样的例子时,例如。https://rocky.github.io/pycon2018.co/#/2.我在问题中得到的一个评论是,你为什么需要这样的东西。
如今,我倾向于只根据自己的个人需求、兴趣和时间来改进事情,而不太担心别人。(毕竟,开源工作应该意味着其他人也可以做出贡献。)