选择在异常中报告哪一行

And*_*den 18 python exception

假设我在Python中有一个多行引发异常.

Python如何决定引发异常的哪一行?

示例:( 注意:我可以\在每行后使用反斜杠)

(1
 +0/0
 +3)
Run Code Online (Sandbox Code Playgroud)

第3行引发异常(ZeroDivisionError异常,at +3)).

(1
 +
 0/0
 )
Run Code Online (Sandbox Code Playgroud)

第3行引发异常.

(0/0
 +
 1)
Run Code Online (Sandbox Code Playgroud)

第2行引发异常.

这个问题的灵感来自于这个例子,@ Godman 指出异常不会发生在最后一行(正如我之前所想的那样).

God*_*Man 5

从根本上说,我不认为我们所有人的想法都是正确的。这里没有最后一行这样的东西。当解释器完全接收到表达式时,会引发异常。根据 Python 语法:http://docs.python.org/reference/grammar.html,只有在按下右大括号 ')' 后,表达式才完全完整。乔兰·比斯利(Joran Beasley)在针对该问题本身的评论中对此做出了简短的解释。

您可以做三件事来判断其正确性,而无需深入研究语法:-

  1. 在 python 解释器中编写以下代码:

    a=(1+2+0/0+4+5)

这也会引发 ZeroDivionError。

  1. 在 python 解释器中编写以下代码:

    a=(1+2+0/0+4+5 # 然后按 Enter

这会给你无效的语法,因为表达式不完整并且无法被解释器解析 PS:这与问题中提到的代码相同

  1. 在 python 解释器中编写以下代码:

a = (1
+2
+0/0
+4
+5)

最终,直到您按下右大括号后,表达式才会完成。因此,您可以继续在其中添加更多子表达式,而不会出现任何异常。因此,从根本上来说,解释器并不将这一切视为行号;而是将其视为行号。它会等到所有表达式(包括子表达式)完成。并且,它是解释器的正确编程控制流程。

PS:请原谅我的答案格式。

新编辑:-

@海登:我认为通过不深入研究语法来解释微妙之处会很容易。但是,供您参考,我只是从 URL 复制代码: http: //nedbatchelder.com/blog/200804/the_struct_of_pyc_files.html

运行步骤:- 1. 将问题中要求的代码写入 temp.py 文件并保存,然后将 temp 导入另一个文件或解释器中。这将创建 temp.pyc 2。现在,将上述 URL 中的完整代码复制并粘贴到 byteCodeDetails.py 中,并在命令提示符中运行该文件:python byteCodeDetails.py temp.pyc。函数 show_file 将在此处调用,并给出以下输出:-

magic 03f30d0a
moddate 458c2e50(2012年8月17日星期五23:54:05)代码
argcount 0
nlocals 0 stacksize 3标志0040代码
640600640200640200151764030017640400175a000064050053 5
0
LOAD_CONST 6 (3)
3 LOAD_CONST 2 (0)
6 LOAD_CONST 2 (0)
9 BINARY_DIVIDE
10 BINARY_ADD
11 LOAD_CONST 3 (4)
14 BINARY_ADD
15 LOAD_CONST 4 (5)
18 BINARY_ADD
19 STORE_NAME 0 (a)
22 LOAD_CONST 5(无)
25 RETURN_VALUE
consts
1
2
0
4
5

3 个
名称 ('a',)
varnames ()
freevars ()
cellvars ()
文件名 'C:\Users\Python\temp1.py'

名称 ''
firstlineno 5
lnotab


因此,正如您所注意到的:-

  1. 引用上面提到的链接: 在反汇编的输出中,最左边的数字(1,2,3)是原始源文件中的行号,接下来的数字(0,3,6,9,...)是指令的字节偏移量。同样,对于您的代码,最左边的数字只有 5,即行号,右侧的列代表编译器为您的代码翻译的助记符(由解释器读取),从而指示表达式如何形成,并且它们的形成被编译代码中的行号值取代。
  2. 第一行no指向5。

现在,只需对 temp.py 文件中的初始代码稍作更改即可:-

a = (1
+2
+0/0
+4 +
5)

现在,再次执行上述 2 个步骤。以下是输出:-

magic 03f30d0a
moddate 0f8e2e50(2012年8月18日星期六00:01:43)
代码
argcount 0
nlocals 0
stacksize 3

标志0040
代码640600640200640200151764030017640400175a000064050053
4
0 LOAD_CONST 6 (3)
3 LOAD_CONST 2 (0)
6 LOAD_CONST 2 (0)
9 BINARY_DIVIDE
10 BINARY_ADD
11 LOAD_CONST 3 (4)
14 BINARY_ADD

5 15 LOAD_CONST 4 (5)
18 BINARY_ADD
19 STORE_NAME 0 (a)
22 LOAD_CONST 5 (无)
25 RETURN_VALUE
consts
1
2
0
4
5

3 个
名称 ('a',)
varnames ()
freevars ()
cellvars ()
filename 'C :\Users\Python\temp1.py'
名称''
firstlineno 4

lnotab 0f01

好吧,现在你可以清楚地看到两件事:-

  1. 字节码由“code 640600640200640200151764030017640400175a000064050053”的下一行所示的两行组成,前缀为“4”和“5”。这表明编译器已经解析了.py文件,并将temp.py中的代码转换为将由解释器运行的2行代码。注意,这里无论表达式是否完整,解释器都会执行第4行的内容
  2. firstlineno的值更改为 4 而不是 5

这个冗长讨论的要点是,无论字节码向解释器指示这是一行的开始位置以及应该为此行执行的相应语句,然后,解释器只运行该行以及接下来写入的相应语句到它。

您问题中的代码显示firstlineno 为5,这就是您在第5 行收到错误的原因。希望这现在有帮助。


And*_*den 1

异常将指向包含以下任一内容的行*

  1. 最后一个运算符(如果前面的文字/运算符导致了异常)。

  2. 最后一个文字(否则即最后一个文字/运算符导致异常)。

但是,如果这不是您看到的行为,则可能是由于您的 py(源)文件之一及其相应的(已编译的)pyc 文件或正在运行的代码(在内存中)存在差异引起的。下面是一个说明性示例。

  • 假设E.py包含:

    def z():
        0/0
    
    Run Code Online (Sandbox Code Playgroud)
  • 从 python 命令行import E(这将编译E.py为字节码:E.pyc,并放入内存中)。

  • 调用E.z(),这将在 中的第 2 行产生异常,z并显示该行0/0- 这里并不奇怪。

  • 返回E.py源文件,在顶部插入两行,然后在第二行插入字符串"oh dear, oh dear"

  • 返回 python 命令行,然后E.z()第二次调用。

  • 异常(第 2 行,在 z 中)现在显示"oh dear, oh dear"

*更新:我没有这方面的参考资料,如果您遇到,请发表评论。我之前以为这只是最后一行!