Pythonデバッガーの使用方法

前書き

ソフトウェア開発において、*デバッグ*は、ソフトウェアの正常な実行を妨げる問題を探して解決するプロセスです。

Pythonデバッガーは、Pythonプログラムのデバッグ環境を提供します。 条件付きブレークポイントの設定、一度に1行ずつソースコードをステップ実行、スタック検査などをサポートします。

Pythonデバッガーと対話的に作業する

Pythonデバッガーは、 + pdb +`と呼ばれるモジュールとして標準のPythonディストリビューションの一部として提供されます。 デバッガも拡張可能であり、クラス `+ Pdb +`として定義されます。 詳細については、https://docs.python.org/3/library/pdb.html [+ pdb +`の公式ドキュメント]をご覧ください。

まず、2つのグローバルhttps://www.digitalocean.com/community/tutorials/how-to-use-variables-in-python-3[variables]、https://を持つ短いプログラムを使用して作業します。ネストされたhttps://www.digitalocean.com/community/tutorials/how-to-construct-を作成するwww.digitalocean.com/community/tutorials/how-to-define-functions-in-python-3 [関数] for-loops-in-python-3 [loop]、および `+ nested_loop()`関数を呼び出す ` if name == 'main ':+`構造。

looping.py

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 `を使用して特定のコマンドの詳細を学習できます。 ` pdb +`コンソールはPythonインタラクティブシェルとは異なることに注意してください。

Pythonデバッガーは、プログラムの最後に達すると自動的に最初からやり直します。 + pdb`コンソールを離れるときはいつでも、コマンド + quit`または `+ exit`を入力してください。 プログラム内の任意の場所でプログラムを明示的に再起動する場合は、コマンド「+ run +」を使用して再起動できます。

デバッガーを使用してプログラムを移動する

Pythonデバッガーでプログラムを操作する場合、 + list、` + step`、および `+ next`コマンドを使用してコード内を移動する可能性があります。 このセクションでは、これらのコマンドについて説明します。

シェル内で、現在の行の前後のコンテキストを取得するためにコマンド「+ list」を入力できます。 上記で表示したプログラム + looping.py +`の最初の行-+ num_list = [500、600、700] +`-は次のようになります。

(Pdb)
 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)
 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)
> /Users/sammy/looping.py(2)<module>()
-> alpha_list = ['x', 'y', 'z']
(Pdb)
(Pdb)
> /Users/sammy/looping.py(2)<module>()
-> alpha_list = ['x', 'y', 'z']
(Pdb)

「+ step 」と「 next 」の違いは、「 step 」が呼び出された関数内で停止するのに対し、「 next +」は呼び出された関数を実行して現在の関数の次の行でのみ停止することです。 関数を操作すると、この違いがわかります。

`+ step `コマンドは、関数の実行に到達するとループを繰り返し処理し、最初に ` print(number)`で数値を印刷してから印刷するため、ループの実行内容を正確に表示します。 ` print(letter)+`の文字、数字に戻るなど:

(Pdb)
> /Users/sammy/looping.py(5)<module>()
-> def nested_loop():
(Pdb)
> /Users/sammy/looping.py(11)<module>()
-> if __name__ == '__main__':
(Pdb)
> /Users/sammy/looping.py(12)<module>()
-> nested_loop()
(Pdb)
--Call--
> /Users/sammy/looping.py(5)nested_loop()
-> def nested_loop():
(Pdb)
> /Users/sammy/looping.py(6)nested_loop()
-> for number in num_list:
(Pdb)
> /Users/sammy/looping.py(7)nested_loop()
-> print(number)
(Pdb)
500
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb)
> /Users/sammy/looping.py(9)nested_loop()
-> print(letter)
(Pdb)
x
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb)
> /Users/sammy/looping.py(9)nested_loop()
-> print(letter)
(Pdb)
y
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb)

代わりに、 `+ next `コマンドは、ステップごとのプロセスを表示せずに関数全体を実行します。 ` exit +`コマンドで現在のセッションを終了してから、デバッガーを再び開始しましょう。

python -m pdb looping.py

これで、 `+ next +`コマンドを使用できます。

(Pdb)
> /Users/sammy/looping.py(5)<module>()
-> def nested_loop():
(Pdb)
> /Users/sammy/looping.py(11)<module>()
-> if __name__ == '__main__':
(Pdb)
> /Users/sammy/looping.py(12)<module>()
-> nested_loop()
(Pdb)
500
x
y
z
600
x
y
z
700
x
y
z
--Return--
> /Users/sammy/looping.py(12)<module>()->None
-> nested_loop()
(Pdb)

コードの実行中に、変数に渡された値を調べることができます。これは、 + pp +`コマンドで実行できます。これは、https://docs.pythonを使用して式の値をきれいに出力します。 org / 3 / library / pprint.html#module-pprint [+ pprint +` module]:

(Pdb)
[500, 600, 700]
(Pdb)

`+ pdb `のほとんどのコマンドには、より短いエイリアスがあります。 「 step 」の場合、その短い形式は「 s 」であり、「 next 」の場合は「 n 」です。 ` help `コマンドは利用可能なエイリアスをリストします。 プロンプトで ` ENTER +`キーを押すことで最後に呼び出したコマンドを呼び出すこともできます。

ブレークポイント

通常、上記の例よりも大きなプログラムで作業するため、プログラム全体を調べるのではなく、特定の機能や行を調べたいと思うでしょう。 `+ break +`コマンドを使用してブレークポイントを設定することにより、指定したブレークポイントまでプログラムを実行します。

ブレークポイントを挿入すると、デバッガーはブレークポイントに番号を割り当てます。 ブレークポイントに割り当てられる番号は、番号1で始まる連続した整数であり、ブレークポイントを操作するときに参照できます。

以下に示すように、 `+ <program_file>:<line_number> +`の構文に従って、ブレークポイントを特定の行番号に配置できます。

(Pdb)
Breakpoint 1 at /Users/sammy/looping.py:5
(Pdb)

「+ clear 」と入力してから「 y +」と入力して、現在のすべてのブレークポイントを削除します。 次に、関数が定義されている場所にブレークポイントを配置できます。

(Pdb)
Breakpoint 1 at /Users/sammy/looping.py:5
(Pdb)

現在のブレークポイントを削除するには、「+ clear 」と入力してから「 y +」と入力します。 条件を設定することもできます:

(Pdb)
Breakpoint 1 at /Users/sammy/looping.py:7
(Pdb)

ここで、「+ continue 」コマンドを発行すると、「 number 」「 x +」が500より大きいと評価されると(つまり、2番目の反復で600外側のループ):

(Pdb)
500
x
y
z
> /Users/sammy/looping.py(7)nested_loop()
-> print(number)
(Pdb)

現在実行するように設定されているブレークポイントのリストを表示するには、引数なしでコマンド `+ break +`を使用します。 設定したブレークポイントの特性に関する情報を受け取ります:

(Pdb)
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)
Breakpoint 2 at /Users/sammy/looping.py:11
(Pdb)
Disabled breakpoint 1 at /Users/sammy/looping.py:7
(Pdb)
Num Type         Disp Enb   Where
1   breakpoint   keep     at /Users/sammy/looping.py:7
   stop only if number > 500
   breakpoint already hit 2 times
2   breakpoint   keep    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 1 + `のように) + ignore + コマンドでプログラムの現在の反復中にブレークポイントを無視する、 + commands + コマンドでブレークポイントで発生するアクションをトリガーする( コマンド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 "+`に含まれる文字のリストを作成する小さなプログラムを使用します。

letter_list.py

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デバッガーを使用して、最初のサイクルの後に最初に*先にジャンプ*して実行を変更する方法を示しましょう。 これを行うと、https://www.digitalocean.com/community/tutorials/how-to-construct-for-loops-in-python-3 [`+ for +`の中断があることに気付くでしょうループ]:

python -m pdb letter_list.py
> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb)
 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)
Breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb)
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb)
's'
(Pdb)
['s']
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb)
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb)
'a'
(Pdb)
Disabled breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb)
['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)
 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)
Breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb)
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb)
's'
(Pdb)
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb)
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb)
's'
(Pdb)
Disabled breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb)
['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 」に追加された「」の2つの値が表示されます。

一部のジャンプは、特に未定義の特定のフロー制御ステートメントに出入りする場合、デバッガーによって防止されます。 たとえば、引数が定義される前に関数にジャンプしたり、 `+ try:except `ステートメントの途中にジャンプしたりすることはできません。 また、 ` finally +`ブロックから飛び出すこともできません。

Pythonデバッガーの `+ jump +`ステートメントを使用すると、プログラムのデバッグ中に実行フローを変更して、フロー制御をさまざまな目的に変更できるかどうかを確認したり、コードで発生している問題をよりよく理解したりできます。

一般的な `+ pdb +`コマンドの表

Pythonデバッガーでの作業中に覚えておくべき便利な `+ pdb +`コマンドとその短い形式の表を以下に示します。

Command Short form What it does

args

a

Print the argument list of the current function

break

b

Creates a breakpoint (requires parameters) in the program execution

continue

c or cont

Continues program execution

help

h

Provides list of commands or help for a specified command

jump

j

Set the next line to be executed

list

l

Print the source code around the current line

next

n

Continue execution until the next line in the current function is reached or returns

step

s

Execute the current line, stopping at first possible occasion

pp

pp

Pretty-prints the value of the expression

quit or exit

q

Aborts the program

return

r

Continue execution until the current function returns

Pythonデバッガーのドキュメントからコマンドとデバッガーの操作の詳細を読むことができます。

結論

デバッグは、ソフトウェア開発プロジェクトの重要なステップです。 Pythonデバッガーの `+ pdb +`は、Pythonで作成されたプログラムで使用できるインタラクティブなデバッグ環境を実装します。

プログラムを一時停止し、変数に設定されている値を確認し、個別のステップバイステップ方式でプログラムを実行できる機能を使用すると、プログラムの実行内容をより完全に理解し、存在するバグを見つけることができますロジックまたは既知の問題のトラブルシューティング。