2012-08-16 18 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