2012-08-16 43 views
18

假设我有一个Python中的多行引发异常。选择哪条线报告例外

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

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

(1 
+0/0 
+3) 

Thows上线3(一个ZeroDivisionError例外,在+3))异常。

(1 
+ 
0/0 
) 

抛出上线3异常。

(0/0 
+ 
1) 

抛出上线2异常。

此问题受this example和@Godman pointed out启发,异常不会发生在最后一行(正如我以前的想法)。

+0

你知道,当我评论其他问题时,我想知道同样的事情。 – 2012-08-16 16:39:42

+0

在这种情况下,它可能是其他一些非常本地化的原因(例如,.py文件和.pyc文件可能存在差异)。 – 2012-08-16 16:40:39

+7

通常,这是最后一行。如果源文件和实际运行的代码不同步,则可能是任何行。你可以使用'dis.dis()'来查看每个字节码指令的行号。 – 2012-08-16 16:40:51

回答

1

异常将指向含有行*或者:

  1. 最后操作者(如果先前的文字/运营商引起异常)。

  2. 最后一个文字(否则即最后一个文字/操作符引起异常)。

然而,如果这不是你看到的,它可以可以通过差异在您的PY之一引起的(源)文件,无论是其对应的(编译)PYC文件或运行代码的行为(在记忆中)。以下是一个说明性示例。

  • 假设E.py包含:

    def z(): 
        0/0 
    
  • 从Python命令行中,import E(这将编译E.py成字节码:E.pyc,并把到存储器)。

  • 呼叫E.z(),这将在z产生异常,第2行,显示行0/0 - 难怪这里。

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

  • 回到python命令行,并再次调用E.z()

  • 例外(在第2行,z)现在显示"oh dear, oh dear"

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

+0

如果'.py'和'.pyc'文件不同步或不重要,重要的是如果'.py'文件与内存中的实际运行代码同步 – 2012-08-17 15:01:39

+0

谢谢@SvenMarnach,我没有意识到这是在内存中。 – 2012-08-17 15:10:26

4

基本上,我不认为我们都在想正确的路线。这里没有最后一行这样的东西。解释者在完全接收到表达式时引发异常。根据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#并且,按输入

这由于表达式不完整并且不能由解释器进行解析,因此给出了无效的语法。 PS:这与问题

012中提到的代码相同
  1. 写入该代码在python解释:

A =(1
+0/0
5)

最后,表达式只有在您敲上右大括号时才会完成。因此,您可以继续在其中添加更多的子表达式,而不会发生任何异常。因此,从根本上讲,解释者并没有将这一切看作行号;它会等到所有表达式(包括子表达式)完成。而且,这是一个适合编程人员的编程控制流程。 PS:请原谅我对格式的回答。

新编辑: -

@海登:我认为这将是很容易通过不深入钻研语法解释的细微之处。但是,仅供您参考,我只是从URL中复制代码:http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html

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

魔术03f30d0a
创建ModDate 458c2e50(星期五8月17日23时54分05秒2012)代码
argcount 0
nlocals 0堆栈大小3个标志0040码
640600640200640200151764030017640400175a000064050053 5
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(一)
22 LOAD_CONST 5(无)
25 RETURN_VALUE
consts

名称( 'A',)
varnames()
freevars()
cellvars()
文件名 'C:\用户\的Python \ temp1.py'

名称 ''
firstlineno 5
lnotab


所以,你可以注意到: -

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

现在,只要在temp.py文件在你的初始代码略有变化: -

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

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

魔03f30d0a
创建ModDate 0f8e2e50(星期六年08月18 0时01分43秒2012)
代码
argcount 0
nlocals 0
STACKSIZE 3

标志0040
代码 640600640200640200151764030017640400175a000064050053 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(一)
22 LOAD_CONST 5(无)
25 RETURN_VALUE
consts
点 无
名称( 'A',)
varnames()
freevars()
cellvars()
文件名 'C:\用户\的Python \ temp1.py'
名称 ''
firstlineno 4

lnotab 0F01

好了,现在ÿ OU可以清楚地看到两件事情: -

  1. 字节码是由在下一行所示的两行“代码640600640200640200151764030017640400175a000064050053”,由“4”和“5”前缀。这表明编译器已经解析了.py文件并将temp.py中的代码转换为将由解释器运行的两行代码。注意,这里第4行中的内容将被解释器执行不管表达的是完整的或不
  2. firstlineno变化4代替5

的整点的值这个冗长的讨论是,无论字节码告诉解释器哪里是一行开始的地方,以及该行应该执行的相应语句,解释器都会运行该行并在其旁边写入相应的语句。

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

+0

你是对的,在某种意义上,“行”可以通过括号保持不变。我不认为这会改变任何“根本性”的东西,但我喜欢这个双关语。:) – 2012-08-17 12:24:46

+0

@ Hayden:我不确定你是否同意答案 – GodMan 2012-08-17 13:10:08

+0

我不同意,因为**是**的最后一个概念(即表达式)行:*如果表达式使用圆括号编写,则“最后一行”仅仅是包含最后一个“*”的表达式。* **语法错误**似乎显示最后一个l线路像其他例外...... – 2012-08-17 13:24:45