Pythonデバッガーの使用方法
序章
ソフトウェア開発では、デバッグは、ソフトウェアの正常な実行を妨げる問題を探して解決するプロセスです。
Pythonデバッガーは、Pythonプログラムのデバッグ環境を提供します。 条件付きブレークポイントの設定、一度に1行ずつソースコードをステップ実行する、スタック検査などをサポートします。
前提条件
Python 3をインストールし、コンピューターまたはサーバーにプログラミング環境をセットアップする必要があります。 プログラミング環境をセットアップしていない場合は、ローカルプログラミング環境またはサーバー上のプログラミング環境のインストールおよびセットアップガイドを参照して、オペレーティングに適したものにすることができます。システム(Ubuntu、CentOS、Debianなど)
Pythonデバッガーとの対話型の作業
Pythonデバッガーは、pdb
と呼ばれるモジュールとして標準のPythonディストリビューションの一部として提供されます。 デバッガーも拡張可能であり、クラスPdb
として定義されています。 詳細については、pdbの公式ドキュメントをお読みください。
情報:このチュートリアルのサンプルコードに従うには、python3
コマンドを実行して、ローカルシステムでPythonインタラクティブシェルを開きます。 次に、>>>
プロンプトの後に例を追加して、例をコピー、貼り付け、または編集できます。
まず、2つのグローバル変数、ネストされたループを作成する関数、およびif __name__ == '__main__':
を持つ短いプログラムを使用します。 nested_loop()
関数を呼び出す構造。
num_list = [500, 600, 700]
alpha_list = ['x', 'y', 'z']
def nested_loop():
for number in num_list:
print(number)
for letter in alpha_list:
print(letter)
if __name__ == '__main__':
nested_loop()
これで、次のコマンドを使用して、Pythonデバッガーを介してこのプログラムを実行できます。
- python -m pdb looping.py
-m
コマンドラインフラグは、Pythonモジュールをインポートし、スクリプトとして実行します。 この場合、pdb
モジュールをインポートして実行し、上記のようにコマンドに渡します。
このコマンドを実行すると、次の出力が表示されます。
Output> /Users/sammy/looping.py(1)<module>()
-> num_list = [500, 600, 700]
(Pdb)
出力の最初の行には、現在のモジュール名(<module>
で示されている)とそれに続く印刷された行番号(この場合は1
ですが、存在する場合)が含まれます。コメントまたはその他の実行不可能な行である場合は、より大きな数値になる可能性があります)。 2行目は、pdb
がデバッグ用のインタラクティブなコンソールを提供するため、ここで実行されるソースコードの現在の行を示しています。 コマンドhelp
を使用してそのコマンドを学習し、help command
を使用して特定のコマンドの詳細を学習できます。 pdb
コンソールはPythonインタラクティブシェルとは異なることに注意してください。
Pythonデバッガーは、プログラムの最後に到達すると自動的に最初からやり直します。 pdb
コンソールを終了するときはいつでも、コマンドquit
またはexit
を入力してください。 プログラム内の任意の場所でプログラムを明示的に再起動する場合は、コマンドrun
を使用して再起動できます。
デバッガーを使用してプログラム内を移動する
Pythonデバッガーでプログラムを操作する場合、list
、step
、およびnext
コマンドを使用してコード内を移動する可能性があります。 このセクションでは、これらのコマンドについて説明します。
シェル内で、コマンドlist
を入力して、現在の行のコンテキストを取得できます。 上に表示したプログラムlooping.py
の最初の行(num_list = [500, 600, 700]
)から、次のようになります。
(Pdb) list
1 -> num_list = [500, 600, 700]
2 alpha_list = ['x', 'y', 'z']
3
4
5 def nested_loop():
6 for number in num_list:
7 print(number)
8 for letter in alpha_list:
9 print(letter)
10
11 if __name__ == '__main__':
(Pdb)
現在の行は文字->
で示されます。この場合、これはプログラムファイルの最初の行です。
これは比較的短いプログラムであるため、list
コマンドでほぼすべてのプログラムを受け取ります。 引数を指定しない場合、list
コマンドは現在の行の周囲に11行を提供しますが、次のように含める行を指定することもできます。
(Pdb) list 3, 7
3
4
5 def nested_loop():
6 for number in num_list:
7 print(number)
(Pdb)
ここでは、コマンドlist 3, 7
を使用して、3〜7行目を表示するように要求しました。
プログラムを1行ずつ移動するには、step
またはnext
を使用できます。
(Pdb) step
> /Users/sammy/looping.py(2)<module>()
-> alpha_list = ['x', 'y', 'z']
(Pdb)
(Pdb) next
> /Users/sammy/looping.py(2)<module>()
-> alpha_list = ['x', 'y', 'z']
(Pdb)
step
とnext
の違いは、step
は呼び出された関数内で停止するのに対し、next
は呼び出された関数を実行して次の行でのみ停止することです。現在の機能。 関数を操作すると、この違いがわかります。
step
コマンドは、関数の実行に到達するとループを繰り返し、ループが実行していることを正確に示します。最初にprint(number)
で数値を出力し、次に出力を実行します。 print(letter)
の文字、数字に戻るなど:
(Pdb) step
> /Users/sammy/looping.py(5)<module>()
-> def nested_loop():
(Pdb) step
> /Users/sammy/looping.py(11)<module>()
-> if __name__ == '__main__':
(Pdb) step
> /Users/sammy/looping.py(12)<module>()
-> nested_loop()
(Pdb) step
--Call--
> /Users/sammy/looping.py(5)nested_loop()
-> def nested_loop():
(Pdb) step
> /Users/sammy/looping.py(6)nested_loop()
-> for number in num_list:
(Pdb) step
> /Users/sammy/looping.py(7)nested_loop()
-> print(number)
(Pdb) step
500
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb) step
> /Users/sammy/looping.py(9)nested_loop()
-> print(letter)
(Pdb) step
x
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb) step
> /Users/sammy/looping.py(9)nested_loop()
-> print(letter)
(Pdb) step
y
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb)
代わりに、next
コマンドは、段階的なプロセスを表示せずに関数全体を実行します。 exit
コマンドを使用して現在のセッションを終了してから、デバッガーを再開してみましょう。
- python -m pdb looping.py
これで、next
コマンドを使用できます。
(Pdb) next
> /Users/sammy/looping.py(5)<module>()
-> def nested_loop():
(Pdb) next
> /Users/sammy/looping.py(11)<module>()
-> if __name__ == '__main__':
(Pdb) next
> /Users/sammy/looping.py(12)<module>()
-> nested_loop()
(Pdb) next
500
x
y
z
600
x
y
z
700
x
y
z
--Return--
> /Users/sammy/looping.py(12)<module>()->None
-> nested_loop()
(Pdb)
コードを調べているときに、変数に渡された値を調べたい場合があります。これは、pp
コマンドで実行できます。このコマンドは、
(Pdb) pp num_list
[500, 600, 700]
(Pdb)
pdb
のほとんどのコマンドのエイリアスは短くなっています。 step
の場合、その短縮形はs
であり、next
の場合はn
です。 help
コマンドは、使用可能なエイリアスを一覧表示します。 プロンプトでENTER
キーを押して、最後に呼び出したコマンドを呼び出すこともできます。
ブレークポイント
通常、上記の例よりも大きなプログラムで作業するため、プログラム全体を調べるのではなく、特定の関数または行を確認することをお勧めします。 break
コマンドを使用してブレークポイントを設定することにより、指定されたブレークポイントまでプログラムを実行します。
ブレークポイントを挿入すると、デバッガーはそれに番号を割り当てます。 ブレークポイントに割り当てられた番号は、番号1で始まる連続した整数であり、ブレークポイントを操作するときに参照できます。
ブレークポイントは、次のように<program_file>:<line_number>
の構文に従うことにより、特定の行番号に配置できます。
(Pdb) break looping.py:5
Breakpoint 1 at /Users/sammy/looping.py:5
(Pdb)
clear
と入力し、次にy
と入力して、現在のすべてのブレークポイントを削除します。 次に、関数が定義されている場所にブレークポイントを配置できます。
(Pdb) break looping.nested_loop
Breakpoint 1 at /Users/sammy/looping.py:5
(Pdb)
現在のブレークポイントを削除するには、clear
と入力してから、y
と入力します。 条件を設定することもできます。
(Pdb) break looping.py:7, number > 500
Breakpoint 1 at /Users/sammy/looping.py:7
(Pdb)
ここで、continue
コマンドを発行すると、number
x
が500より大きいと評価されたとき(つまり、600に設定されたとき)にプログラムが中断します。外側のループの2回目の反復で):
(Pdb) continue
500
x
y
z
> /Users/sammy/looping.py(7)nested_loop()
-> print(number)
(Pdb)
現在実行するように設定されているブレークポイントのリストを表示するには、引数なしでコマンドbreak
を使用します。 設定したブレークポイントの特殊性に関する情報を受け取ります。
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at /Users/sammy/looping.py:7
stop only if number > 500
breakpoint already hit 2 times
(Pdb)
コマンドdisable
とブレークポイントの番号を使用してブレークポイントを無効にすることもできます。 このセッションでは、別のブレークポイントを追加してから、最初のブレークポイントを無効にします。
(Pdb) break looping.py:11
Breakpoint 2 at /Users/sammy/looping.py:11
(Pdb) disable 1
Disabled breakpoint 1 at /Users/sammy/looping.py:7
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep no at /Users/sammy/looping.py:7
stop only if number > 500
breakpoint already hit 2 times
2 breakpoint keep yes at /Users/sammy/looping.py:11
(Pdb)
ブレークポイントを有効にするには、enable
コマンドを使用し、ブレークポイントを完全に削除するには、clear
コマンドを使用します。
(Pdb) enable 1
Enabled breakpoint 1 at /Users/sammy/looping.py:7
(Pdb) clear 2
Deleted breakpoint 2 at /Users/sammy/looping.py:11
(Pdb)
pdb
のブレークポイントは、多くの制御を提供します。 いくつかの追加機能には、ignore
コマンドを使用したプログラムの現在の反復中にブレークポイントを無視すること(ignore 1
のように)、commands
コマンドを使用してブレークポイントでアクションを発生させることが含まれます( command 1
のように)、プログラムの実行がコマンドtbreak
でポイントに最初に到達したときに自動的にクリアされる一時的なブレークポイントを作成します(たとえば、3行目の一時的なブレークの場合は次のように入力できます) tbreak 3
)。
pdb
をプログラムに統合する
pdb
モジュールをインポートし、セッションを開始する行の上にpdb
関数pdb.set_trace()
を追加することで、デバッグセッションをトリガーできます。
上記のサンプルプログラムでは、import
ステートメントと、デバッガーに入力する関数を追加します。 この例では、ネストされたループの前に追加しましょう。
# Import pdb module
import pdb
num_list = [500, 600, 700]
alpha_list = ['x', 'y', 'z']
def nested_loop():
for number in num_list:
print(number)
# Trigger debugger at this line
pdb.set_trace()
for letter in alpha_list:
print(letter)
if __name__ == '__main__':
nested_loop()
コードにデバッガーを追加することで、特別な方法でプログラムを起動したり、ブレークポイントを設定したりする必要がなくなります。
pdb
モジュールをインポートしてpdb.set_trace()
関数を実行すると、通常どおりプログラムを開始し、デバッガーを実行して実行できます。
プログラム実行フローの変更
Pythonデバッガーを使用すると、jump
コマンドを使用して実行時にプログラムのフローを変更できます。 これにより、前方にスキップして一部のコードが実行されないようにしたり、後方に戻ってコードを再度実行したりできます。
文字列sammy = "sammy"
に含まれる文字のリストを作成する小さなプログラムを使用します。
def print_sammy():
sammy_list = []
sammy = "sammy"
for letter in sammy:
sammy_list.append(letter)
print(sammy_list)
if __name__ == "__main__":
print_sammy()
python letter_list.py
コマンドを使用して通常どおりプログラムを実行すると、次の出力が返されます。
Output['s']
['s', 'a']
['s', 'a', 'm']
['s', 'a', 'm', 'm']
['s', 'a', 'm', 'm', 'y']
Pythonデバッガーを使用して、最初のサイクルの後に最初にジャンプして実行する方法を示しましょう。 これを行うと、forループが中断されていることがわかります。
- python -m pdb letter_list.py
> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb) list
1 -> def print_sammy():
2 sammy_list = []
3 sammy = "sammy"
4 for letter in sammy:
5 sammy_list.append(letter)
6 print(sammy_list)
7
8 if __name__ == "__main__":
9 print_sammy()
10
11
(Pdb) break 5
Breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb) continue
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb) pp letter
's'
(Pdb) continue
['s']
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb) jump 6
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb) pp letter
'a'
(Pdb) disable 1
Disabled breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb) continue
['s']
['s', 'm']
['s', 'm', 'm']
['s', 'm', 'm', 'y']
上記のデバッグセッションでは、コードが続行されないように5行目にブレークを入れてから、コードを続行します(letter
の値をきれいに出力して、何が起こっているかを示します)。 次に、jump
コマンドを使用して6行目にスキップします。 この時点で、変数letter
は文字列'a'
と等しく設定されていますが、それをリストsammy_list
に追加するコードをジャンプします。 次に、ブレークポイントを無効にして、continue
コマンドで通常どおり実行を続行します。そのため、'a'
がsammy_list
に追加されることはありません。
次に、この最初のセッションを終了し、デバッガーを再起動してプログラム内でジャンプバックして、すでに実行されているステートメントを再実行します。 今回は、デバッガーでfor
ループの最初の反復を再度実行します。
> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb) list
1 -> def print_sammy():
2 sammy_list = []
3 sammy = "sammy"
4 for letter in sammy:
5 sammy_list.append(letter)
6 print(sammy_list)
7
8 if __name__ == "__main__":
9 print_sammy()
10
11
(Pdb) break 6
Breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb) continue
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb) pp letter
's'
(Pdb) jump 5
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb) continue
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb) pp letter
's'
(Pdb) disable 1
Disabled breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb) continue
['s', 's']
['s', 's', 'a']
['s', 's', 'a', 'm']
['s', 's', 'a', 'm', 'm']
['s', 's', 'a', 'm', 'm', 'y']
上記のデバッグセッションでは、6行目にブレークを追加し、続行した後、5行目にジャンプして戻りました。 文字列's'
がリストsammy_list
に2回追加されていることを示すために、途中できれいに印刷しました。 次に、6行目のブレークを無効にして、プログラムの実行を続行しました。 出力には、sammy_list
に追加された's'
の2つの値が表示されます。
一部のジャンプは、特に未定義の特定のフロー制御ステートメントにジャンプしたりジャンプしたりするときに、デバッガーによって防止されます。 たとえば、引数が定義される前に関数にジャンプしたり、try:except
ステートメントの途中にジャンプしたりすることはできません。 finally
ブロックから飛び出すこともできません。
Pythonデバッガーのjump
ステートメントを使用すると、プログラムのデバッグ中に実行フローを変更して、フロー制御をさまざまな目的に変更できるかどうかを確認したり、コードで発生している問題をよりよく理解したりできます。
一般的なpdb
コマンドの表
これは、Pythonデバッガーで作業するときに覚えておくと便利なpdb
コマンドとその短い形式の表です。
指示 | ショートフォーム | それが何をするか |
---|---|---|
args |
a |
現在の関数の引数リストを出力します |
break |
b |
プログラムの実行時にブレークポイントを作成します(パラメーターが必要です) |
continue |
c またはcont |
プログラムの実行を継続します |
help |
h |
指定されたコマンドのコマンドまたはヘルプのリストを提供します |
jump |
j |
実行する次の行を設定します |
list |
l |
現在の行の周りにソースコードを印刷します |
next |
n |
現在の関数の次の行に到達するか、戻るまで実行を続けます |
step |
s |
現在の行を実行し、最初の可能な機会に停止します |
pp |
pp |
式の値をきれいに印刷します |
quit またはexit |
q |
プログラムを中止します |
return |
r |
現在の関数が戻るまで実行を続けます |
コマンドとデバッガーの操作の詳細については、Pythonデバッガーのドキュメントを参照してください。
結論
デバッグは、ソフトウェア開発プロジェクトの重要なステップです。 Pythonデバッガーpdb
は、Pythonで記述された任意のプログラムで使用できるインタラクティブなデバッグ環境を実装しています。
プログラムを一時停止し、変数が設定されている値を確認し、個別のステップバイステップでプログラムの実行を実行できる機能を使用すると、プログラムの実行内容をより完全に理解し、に存在するバグを見つけることができます。ロジックまたは既知の問題のトラブルシューティング。