return
が含まれていないメソッドf()
をデバッグしています。
class A(object):
def __init__(self):
self.X = []
def f(self):
for i in range(10):
self.X.append(i)
このメソッドが呼び出された直後に変数X
をどのように変更するかを確認する必要があります。これを行うには、メソッドの最後にreturn
を挿入し、そこにブレークポイントを設定します。
そうすれば、メソッドがreturn
に達するとすぐに、変数X
の値を確認できます。
これでうまくいきますが、もっと良い方法があると確信しています。デバッグが必要になるたびにメソッドや関数を編集するのはばかげているようです。
質問:return
を持たないメソッドの最後にブレークポイントを設定する別の方法(デバッガーのオプションなど)はありますか?
(関数呼び出しでブレークポイントを設定し、を使用することに注意してください Step Over 関数は別のモジュールから呼び出されるため、マウスオーバー時にX
は表示されません。)
最後の行に条件付きブレークポイントを追加し、最後の反復でのみ発生する条件に設定できます。
この場合、条件はi == 9
であるため非常に簡単ですが、ループ条件によってははるかに複雑になる可能性があるため、最後にステートメントを追加する方が簡単な解決策になる場合があります。
そのスクリーンショットはIntelliJ IDEAからのものであり、スクリーンショットは同じIDEからのもののように見えるので、ブレークポイントを右クリックしてダイアログを表示し、条件を入力します。
他のIDEを使用している場合は、ブレークポイントを条件付きにする機能があると確信しています。
更新:
Pythonデバッガー のメソッドの最後でのブレークはサポートされていません。メソッドの開始時のみです。
b(reak)[[filename:] lineno |関数[、条件]]
Lineno引数を使用して、現在のファイルにブレークを設定します。関数の引数を使用して、その関数内の最初の実行可能ステートメントにブレークを設定します。別のファイル(おそらくまだロードされていないファイル)のブレークポイントを指定するために、行番号の前にファイル名とコロンを付けることができます。ファイルはsys.pathで検索されます。各ブレークポイントには、他のすべてのブレークポイントコマンドが参照する番号が割り当てられていることに注意してください。
2番目の引数が存在する場合、それはブレークポイントが受け入れられる前にtrueと評価される必要がある式です。
引数なしで、各ブレークポイント、ブレークポイントがヒットした回数、現在の無視カウント、および関連する条件(存在する場合)を含むすべてのブレークをリストします。
あなたのIDEは、内部にあるものを隠しています。つまり、次のようなものです。
_import pdb
_
スクリプトの前に追加され、
_pdb.set_trace()
_
ブレークポイントを配置した行の前に挿入されます。あなたの言うことから、PyCharmは空の行にブレークポイントを置くのが好きではないと推測します。ただし、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
を使用できます。
モンキーパッチをサポートするany言語(Python、Ruby、ObjCなど)で動作するquick&dirtyソリューションがあります。正直なところ、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
_
ほとんどのスクリプト可能なデバッガーでは、名前でメソッドに対してこれを自動的に行うスクリプトを記述できるはずです。通常のPythonコードをデバッガーで直接実行できるpdbでは、特に簡単です。
これで、その_return result
_にブレークポイントを設定でき、実際の_A.f
_が戻った直後にヒットすることが保証されます(途中で戻ったり、return
なしで最後から外れたりした場合でも)ステートメント)。
追加したいことがいくつかあります。
A.f
_のレイズもキャッチしたい場合は、コードの周りに_try:
_と_except: raise
_を置き、raise
にブレークポイントを追加します。A
インスタンスにのみブレークポイントが必要な場合は、_self is a
_をチェックする条件付きブレークポイントを使用するか、_types.MethodType
_を使用してバインドされたインスタンスを作成し、それを_a.f
_。functools.wraps
_を使用することをお勧めします。wrap_method
_関数をプロジェクトのどこかに配置し、プロンプトでp utils.wrap_method(A, 'f')
と記述できます。ただし、この方法で複数のメソッドをラップすると、それらは同じブレークポイントを共有します(_wrap_method
_内で定義されたラッパー関数内)。ここでは、条件付きブレークポイントが唯一の合理的なオプションだと思います。exec(real_A_f.__code__, real_A_f.globals())
など)を思いつくことができますが、満足できるものは何もありません。pdb
を使用すると、 _break function
_ と _until 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--
_
ご覧のとおり、pre execとpost 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')
_
以前と同様の方法で開始します。
_> 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)
_
ここでの悲しいことは、この自動化を試してみたかったことです。
_(Pdb) break A.f
Breakpoint 1 at d:\tmp\stack\test.py:5
(Pdb) commands 1
(com) disable 1
(com) until 11
(com) end
_
これは、必要なすべてを自動的に実行します(ここでも、少なくとも1つのプレループステートメントがある場合は_disable 1
_は必要ありません)が、 commands
のドキュメントによると:
実行を再開するコマンド(現在、continue、step、next、return、jump、quit、およびそれらの省略形)を指定すると、コマンドリストが終了します(そのコマンドの直後にendが続くかのように)。これは、実行を再開するたびに(単純な次またはステップでも)、別のブレークポイントが発生する可能性があるためです。このブレークポイントには独自のコマンドリストがあり、実行するリストがあいまいになる可能性があります。
したがって、until
は機能していないようです(少なくともPython 3.2.5ではWindowsの場合))。これは手動で行う必要があります。
ここにはいくつかのオプションがあります。
この場合、最後の行はループ内にあるため、ループ内の各アイテムを反復処理する必要があります。
これにより、関数が呼び出される前にデバッガーが停止しますが、関数を「ステップオーバー」して、A.f()
が呼び出された後にA.x
の値を確認できます。
このトリックは、関数がループで終了し、関数が呼び出される場所が複数ある場合、または関数呼び出しを追跡したくない場合に機能します。
デバッグの目的で関数の最後に簡単なステートメントを追加し、そこにブレークポイントを追加できます。
def f(self):
for i in range(10):
self.X.append(i)
debug_break = 1
リターンをそのままにしておかないのはなぜですか?またはreturn None
。とにかくそれは暗黙的です、インタプリタ/コンパイラは関係なく同じことをします:
実際、returnステートメントのない関数でさえ、かなり退屈なものではありますが、値を返します。この値はNoneと呼ばれます(組み込みの名前です)。