9. 落穂ひろい
9.1. おまけ: デバッグの手法
デバッグの手法についてです. 長いプログラムを書いていると,思った通りの動作を してくれない場合があります. これらのバグは,たいていの場合,ユーザーの プログラミング言語に対する
認識不足
不注意
思い込み
などが原因です. (プログラムは,通常書いたとおりに動きます.思った通りに動かないだけです)
このような,動作を修正するための方法は, デバッグ と呼ばれ,様々な技法が存在します.
ここでは,簡単な幾つかの方法について紹介します.
9.1.1. print 文による情報表示
これは,割りと汎用的なデバッグ技法で,ほとんどの
プログラミング言語で使用可能な方法です.
プログラムは,書いたとおり動くものなので,プログラムの
動作途中の変数の状態などを print() 関数などを
使って,コンソールに書き出すことでチェックします.
たとえば,''1 から 10 までの和を求めたい'' という状況が 発生したとして,下記のようなプログラムを記述したとしましょう.
sum = 0
for idx in range(10):
sum = sum + idx
このコードを実行すると sum に 45 という数値が代入されますが, 求める答えと違うことになります.(正解は 55)
このコードの,どこにバグがあるのかを考えるために
sum = 0
for idx in range(10):
sum = sum + idx
print('idx: ', idx, 'sum: ', sum)
print(sum)
というようにループ内部に print文を導入して ,変数 sum と idx の状態を表記させてやります.すると
>>> sum = 0
>>> for idx in range(10):
... sum = sum + idx
... print 'idx: ', idx, 'sum: ', sum
idx: 0 sum: 0
idx: 1 sum: 1
idx: 2 sum: 3
idx: 3 sum: 6
idx: 4 sum: 10
idx: 5 sum: 15
idx: 6 sum: 21
idx: 7 sum: 28
idx: 8 sum: 36
idx: 9 sum: 45
>>> print sum
45
といったように,変数 'idx' が,0 から 9 まで変化していることが わかります. 問題としては ''1 から 10 まで和'' を求めたいという話なので, 対応するためには,
>>> sum = 0
>>> for idx in range(10):
... sum = sum + (idx+1)
... print('idx+1: ', (idx+1), 'sum: ', sum)
のように修正してやる必要がありそうです.(他にもやりかたはあります)
この例は, range(M)() 関数の生成するリストが,
0 から M-1 までの整数値を返してくることを誤解しているような
場合に起きうる間違いです.
このように, print() 関数を用いることで,
状況を確認することができます.
9.1.2. デバッガによる確認
print() 関数によるデバッグは有効ですが,
大量のデータのデバッグや,オブジェクトの内部状態を知りたいなど
といった場合には,なんらかの工夫が必要になってきます.
継続的に変数やオブジェクトの状況を観察しながら,デバッグを
行いたいといった場合に有効な手法が デバッガ を用いた
デバッグになります.
C などのコンパイラ言語を用いた場合のデバッガに gdb などが
あるように python には pdb というデバッガが標準モジュールとして
添付されます.
9.1.2.1. スクリプトのデバッグ
自分の書いたスクリプトをデバッグする場合には, シェルのコマンドラインから下記のように,pdb を モジュールとして読み込んで実行させれば OK です. 例題として,上で使用した '1〜10 までの和を求める為の計算' プログラムを 例題とし,これが,hoge.py という名前だったとしましょう.
sum = 0
for idx in range(10):
sum = sum + idx
print('idx: ', idx, 'sum: ', sum)
print(sum)
スクリプトのデバッグを実行するには,コンソールで,
$ python -m pdb hoge.py
と実行すれば
> /Users/shouno/hoge.py(1)<module>()
-> sum = 0
(Pdb)
と今までと違ったプロンプトが出ることがわかります. このプロンプトが Pdb デバッガのプロンプトです. Pdb は立ち上がるとスクリプトファイルを読み込み, スクリプトの最初の行で,実行を停止して, ユーザーからのコマンド待ち受け状態になります. ここでは,いくつかのコマンドの簡単な紹介をします.
l (list) コマンド
list コマンドはソースコードを表示します.
(Pdb) list 1 -> sum = 0 2 for idx in range(10): 3 sum = sum + idx 4 print('idx: ', idx, 'sum: ', sum) 5 print(sum) [EOF]ちょうど,矢印 -> の位置が現在の実行している行です.
また,引数として,表示させたい行を指定できます. 3行目付近を見たければ
(Pdb) list 3のように指定できます.
n (next) コマンド
現在の指している行を実行して,次の行に移ります. 上述の例で next コマンドを実行した場合,変数 sum に 0 を設定し,2行目に移動します. したがって,変数名を知りたければ,通常の python の コマンドラインと同様に変数名を打ち込んでやれば 変数値を表示してくれます.
(Pdb) next (Pdb) sum 0b (break) コマンド
break コマンドは,任意の行で実行を止めることが可能です. 下記の continue コマンドと一緒に使うことで,スクリプトの 任意の行まで実行を行い,止めることが出来るようになります. 例えば,4行目まで実行して,そこで止めたい場合, break コマンドの引数として行番号を指定をします. list コマンドで見ると 行頭に B が付きます.
(Pdb) break 4 Breakpoint 1 at /Users/shouno/hoge.py:4 (Pdb) list 4 1 sum = 0 2 -> for idx in range(10): 3 sum = sum + idx 4 B print('idx: ', idx, 'sum: ', sum) 5 print(sum) [EOF]c (continue) コマンド
continue コマンドは,実行を継続します.break point が 設定されていない場合は,プログラムが停止するまで 実行しますので,エラーの位置を知りたい場合は,これを 最初に使うことになります.
上記の例で,continue コマンドを実行すると,4行目に break point が設定されているので,その直前の 3行目まで実行します.
(Pdb) c > /Users/shouno/hoge.py(4)<module>() -> print 'idx: ', idx, 'sum: ', sumつまり,idx = 0 の時点で,止まることになります.
次のループ (idx=1) に移りたい場合は, 同様に continue コマンドを実行します.この場合も, やはり,4行目のブレークポイントの直前で 停止する事になります
9.1.2.2. 関数のデバッグ
それでは,関数やオブジェクトメソッドのデバッグは, どのようにすればよいでしょうか?
クラッシュするプログラムを調べるためには,
関数を実行する前に pdb をモジュールとして import して,
pdb.pm() 関数をつかって,事後解析デバッグを行います.
mymodule が自分で書いたモジュールで,モジュール内の test() という
関数をデバッグする場合,
>>> import pdb
>>> import mymodule
>>> mymodule.test()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "./mymodule.py", line 4, in test test2()
File "./mymodule.py", line3, in test2
print spam
NameError: spam
>>> pdb.pm()
> ./mymodule.py(3).test2()
-> print(spam)
(Pdb)
となって,クラッシュした場所の位置でデバッガに入ることができます. ここで print などで,変数内容などを詳しく調べることができます.
9.1.2.3. pdb についての詳細情報
より詳細に関しては,
PDB Python デバッガ http://docs.python.jp/2/library/pdb.html
日本 Python ユーザー会 pdb の使い方 http://www.python.jp/Zope/Zope/articles/tips/pdb-howto-1
などを参照してください.
個人的には pdb3 あたりを好んで使っています. また,Visual Studio Code や PyCharm で実行するとここらへんの不満がほぼ解消されます.