2015-04-29 43 views
32

我在調試方法f()中沒有return在函數結束時設置調試器斷點而不返回

class A(object): 

    def __init__(self): 
     self.X = [] 

    def f(self):    
     for i in range(10): 
      self.X.append(i) 

我需要看看這個方法在被調用後如何修改變量X。要做到這一點,我在方法的末尾插入return,並設置斷點有:儘快

enter image description here

這樣一來,由於該方法達到return,我可以看到我的變量的值X


這樣做的工作,但我很確定有一個更好的方法。每次我需要調試時編輯一個方法或函數似乎很愚蠢。

問題
是否有不同的方式(例如,在調試器中的一個選項)在方法結束這沒有一個return設置一個斷點?

(請注意,當mouseovering在函數調用設置斷點,並使用步過不會顯示X,因爲該功能從不同的模塊調用。)

+1

這可能不是最好的答案,所以我只是將它作爲註釋發佈,但是有一種快速和骯髒的方法可以用任何語言與monkeypatching(Python,Ruby,ObjC,Smalltalk等)工作:可以動態地將'Af'包裝在一個只返回real_A_dot_f(* args,** kw)的函數中,然後將該斷點放在該行上。我用過gdb/lldb/pdb/etc。腳本在過去爲我打包。 – abarnert

+0

@abarnert我認爲你的評論更適合作爲完整答案。可能有更好的解決方案(我不知道),但如果你的建議有效,它仍然是一個解決方案。 –

+0

我不確定,因爲我認爲它不符合「A.f」框架的當地人仍然活着要檢查的目的,但是我仍然寫了它;如果它對OP沒有用處,他總是可以忽略它,或者甚至會使它失望。 :) – abarnert

回答

0

你這裏有幾個選項。

  1. 添加一個斷點到函數的最後一行。

在這種情況下,最後一行在循環內,因此您必須遍歷循環中的每個項目。

  1. 在函數被調用的地方添加一個斷點。

這將導致調試器停止函數被調用之前,但你可以「步過」的功能,看的A.xA.f()後的值被調用。

  • 添加臨時語句的函數結束時在
  • 這一招打破,如果你的函數在一個循環結束,還有多個地方的功能會工作調用或者你不想追蹤函數調用。

    您可以添加一個簡單的語句到該函數的結尾以進行調試,並在其中添加一箇中斷點。

    def f(self):    
        for i in range(10): 
         self.X.append(i) 
        debug_break = 1 
    
    +0

    1.不起作用,因爲該函數有一個'for'循環。 2.不起作用,因爲該函數是從不同的模塊調用的,所以當我在「跳過」之後將鼠標懸停在「X」上時,無法顯示「X」的值。 3.我正在做什麼。 –

    5

    您可以在最後一行添加條件斷點,並將條件設置爲僅在最後一次迭代中發生的情況。

    在這種情況下,條件非常簡單,因爲它只是i == 9,但根據循環條件它可能會複雜得多,所以有時在最後添加語句將是更簡單的解決方案。

    Conditional breakpoint in IntelliJ IDEA

    這截圖是從IntelliJ IDEA的和你的截圖看起來像來自同一個IDE的,所以只需右鍵單擊該斷點,顯示對話框,輸入您的條件。

    如果您使用的是其他IDE,我相信有能力創造一個有條件的斷點。

    更新:

    有用於破碎在一個方法中Python debugger結束時,僅在方法的開始不支持:

    B(reak)[[文件名:] lineno |函數[,條件]]

    使用lineno參數,在當前文件中設置一箇中斷。使用函數參數,在該函數中的第一個可執行語句處設置一箇中斷。行號可以用文件名和冒號作爲前綴,以指定另一個文件中的斷點(可能是尚未加載的文件)。該文件在sys.path上搜索。請注意,每個斷點都分配了一個其他所有斷點命令引用的數字。

    如果存在第二個參數,那麼它是一個表達式,在判斷斷點之前必須計算爲true。

    如果沒有參數,請列出所有中斷,包括對於每個斷點,斷點已被命中的次數,當前忽略計數以及相關條件(如果有)。

    +0

    也許在PyCharm中不支持,但你絕對**可以**在用pdb(或ipdb)方法結束時中斷。 –

    +0

    然後請解釋如何做到這一點,因爲pdb(我上面引用的)的文檔沒有說明這樣做的任何方式。 – Raniz

    +0

    當然。這是在我的(單獨的)答案中。如果它仍然不清楚,請對此評論。 –

    3

    存在對任何語言工作的快速&骯髒的解決方案,支持的monkeypatching(Python和Ruby,ObjC等)。我真的不記得在Python中需要它,但是我在SmallTalk和ObjC中做了很多,所以也許它對你有用。

    只是動態包A.f的功能,如:

    real_A_f = A.f 
    def wrap_A_f(self, *args, **kwargs): 
        result = real_A_f(self, *args, **kwargs) 
        return result 
    A.f = wrap_A_f 
    

    在大多數編寫腳本調試器,你應該能夠編寫一個腳本,通過名稱的方法自動執行此操作。在pdb中,它允許您在調試器中正確執行普通的Python代碼,這非常簡單。

    現在你可以把一個斷點上return result,它的保證真實A.f返回後立刻打(即使它在中間返回或脫落年底沒有return語句)。

    您可能需要添加幾件事情:

    • 如果你也想趕上A.f認識,把try:except: raise周圍的代碼,並在raise添加一個斷點。
    • 對於Python 2.x,你可能想用types.MethodType來包裝它以構成一個真正的未綁定方法。
    • 如果您只想在特定的A實例上創建斷點,則可以使用條件斷點來檢查self is a,或使用types.MethodType創建綁定實例並將其存儲爲a.f
    • 如果您想要將代碼中的代碼隱藏起來,您可能需要使用functools.wraps(除非您真的想查看代碼),否則您可能需要使用functools.wraps
    • 由於pdb允許您在實時命名空間中正確執行動態代碼,因此可以在項目的某個位置放置一個wrap_method函數,然後在提示符下輸入p utils.wrap_method(A, 'f')。但是如果你用這種方法包裝多個方法,它們將共享相同的斷點(在wrap_method中定義的包裝函數內部)。在這裏我認爲有條件的斷點是唯一合理的選擇。
    • 如果你想從包裝的斷點訪問真正的A.f的本地人,那就更困難了。 也就是說,這是我能想到的一些非常哈克選項(例如,exec(real_A_f.__code__, real_A_f.globals()),但沒有我會很高興。
    4

    你的IDE是隱藏在引擎蓋下是什麼。像

    import pdb 
    

    預先準備給你的腳本和

    pdb.set_trace() 
    

    在其上放置的斷點。 看你說的前行插入我推斷PyCharm不喜歡把BR空線上的eakpoints。然而pdb.set_trace()可以完美地放置在方法的末尾。

    所以你可以自己插入(或寫一個宏)並運行python -m pdb開始調試。

    (編輯)例如

    import pdb 
    
    class A(object): 
    
        def __init__(self): 
         self.X = [] 
    
        def f(self): 
         for i in range(10): 
          self.X.append(i) 
         pdb.set_trace() 
    
    if __name__ == '__main__': 
        a = A() 
        a.f() 
    

    調試與

    $ python -m pdb test.py 
    > /dev/test.py(1)<module>() 
    ----> 1 import pdb 
         2 
         3 class A(object): 
    
    ipdb> cont 
    --Return-- 
    > /dev/test.py(11)f()->None 
    -> pdb.set_trace() 
    (Pdb) self.X 
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
    (Pdb) 
    

    ipdb可以用來代替pdb

    +1

    有一些健康問題,我在賞金到期前的幾分鐘就離開了。由於這是唯一可以解決的答案,因此我在此分配了獎金。我還沒有測試過它。如果它有效,我也會接受這個答案。 –

    +0

    我該如何製作一個在我測試的函數末尾插入'pdb.set_trace()'的宏?它會比在函數的末尾寫入「return」更快嗎?一個例子對未來的讀者會有用。 –

    +0

    例如,Github編輯器[atom](https://atom.io/)的[atom-python-debugger](https://atom.io/packages/atom-python-debugger)已經爲你實現通過按

    2

    隨着pdb,您可以使用漂亮的組合break functionuntil lineno

    沒有參數,繼續執行,直到與多家 大於達到當前行。

    使用行號,繼續執行,直到行號大於或等於 的行。在這兩種情況下,噹噹前幀返回時也停止。

    在版本3.2中更改:允許提供明確的行號。

    你可以實現你所需要的。

    我修改你的榜樣位(所以你會看到,指令獲取雖然PDB報告執行爲「下一個指令」):

    01: class A(object): 
    02: 
    03:  def __init__(self): 
    04:   self.X = [] 
    05:   
    06:  def f(self):   
    07:   print('pre exec') 
    08:   for i in range(10): 
    09:    self.X.append(i) 
    10:   print('post exec') 
    11:    
    12: a = A() 
    13: a.f() 
    14: print('Game is over') 
    15: 
    

    而且從python -m pdb test.py運行結果是這樣的:

    > d:\tmp\stack\test.py(1)<module>() 
    -> class A(object): 
    (Pdb) until 11 
    > d:\tmp\stack\test.py(12)<module>() 
    -> a = A() 
    

    開始調試和剛下課的聲明(這樣你就可以添加一個名爲斷點)後,運行它

    現在,在功能開始打破:

    (Pdb) break A.f 
    Breakpoint 1 at d:\tmp\stack\test.py:6 
    

    與執行就繼續下去,直到它命中斷點:

    (Pdb) continue 
    > d:\tmp\stack\test.py(7)f() 
    -> print('pre exec') 
    

    「也將停止在當前幀返回」

    (Pdb) until 14 
    pre exec 
    post exec 
    --Return-- 
    

    如您所見,前執行後EXEC印,但在執行where當你還在f()

    (Pdb) w 
        c:\python32\lib\bdb.py(405)run() 
    -> exec(cmd, globals, locals) 
        <string>(1)<module>() 
        d:\tmp\stack\test.py(13)<module>() 
    -> a.f() 
    > d:\tmp\stack\test.py(10)f()->None 
    -> print('post exec') 
    > d:\tmp\stack\test.py(10)f()->None 
    -> print('post exec') 
    

    而且所有的環境變量都完好無損:

    (Pdb) p self.X 
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
    

    與現實生活中的例子

    現在:

    01: class A(object): 
    02:  def __init__(self): 
    03:   self.X = [] 
    04:   
    05:  def f(self):   
    06:   for i in range(10): 
    07:    self.X.append(i) 
    08:    
    09: a = A() 
    10: a.f() 
    11: print('Game is over') 
    

    開始類似的時尚a年代以前:

    > d:\tmp\stack\test.py(1)<module>() 
    -> class A(object): 
    (Pdb) until 8 
    > d:\tmp\stack\test.py(9)<module>() 
    -> a = A() 
    (Pdb) break A.f 
    Breakpoint 1 at d:\tmp\stack\test.py:5 
    (Pdb) cont 
    > d:\tmp\stack\test.py(6)f() 
    -> for i in range(10): 
    

    現在...斷點在f.A實際上意味着在f.A先聲明這是不幸for i in...所以它會在它的每一次突破斷點。

    如果你實際上沒有用循環開始你的真實代碼,你可以跳過這部分。

    (Pdb) disable 1 
    Disabled breakpoint 1 at d:\tmp\stack\test.py:5 
    

    再次使用until <end of file>

    (Pdb) until 10 
    --Return-- 
    > d:\tmp\stack\test.py(6)f()->None 
    -> for i in range(10): 
    

    再次,所有的幀變量:

    (Pdb) p i 
    9 
    (Pdb) w 
        c:\python32\lib\bdb.py(405)run() 
    -> exec(cmd, globals, locals) 
        <string>(1)<module>() 
        d:\tmp\stack\test.py(10)<module>() 
    -> a.f() 
    > d:\tmp\stack\test.py(6)f()->None 
    -> for i in range(10): 
    (Pdb) 
    

    悲哀的事情在這裏,我想嘗試這塊自動化:

    這將你需要自動一切按文件(同樣,沒有必要disable 1當你有至少一個環預語句),但commands

    指定任何命令恢復執行(目前繼續,步驟,下一步,返回,跳轉,退出和它們的縮寫)終止命令列表(就像該命令緊接着結束)。這是因爲,無論何時恢復執行(即使是簡單的下一步或步驟),都可能遇到另一個斷點 - 它們可能有自己的命令列表,從而導致關於執行哪個列表的歧義。

    因此until只是似乎不起作用(至少對於Windows 3.2.5下的Windows),你必須手工完成這項工作。

    0

    爲什麼不把回報留在那裏?或者一個return None。這是隱含的,無論如何,解釋器/編譯器會做不顧同一件事:

    事實上,即使沒有功能return語句不返回值,雖然是一個不討人喜歡的。這個值被稱爲無(這是一個內置的名稱)。

    [source: Python Tutorial 4.6]

    +0

    我有很多這樣的功能。正如我的問題所提到的,每當我調試它們時用'return'編輯它們是我想要避免的,如果有更好(更快)的方法來實現我的目標。 –

    +0

    我想說的是在生產中留下那些回報,他們不需要花費任何東西。 – skolsuper

    相關問題