フリーランチ食べたい

No Free Lunch in ML and Life. Pythonや機械学習のことを書きます。

【Python】もうprintデバッグはいらない? / PySnooperで楽々デバッキング

Hacker NewsとRedditでバズっていたPythonのデバッグツールが便利だったので紹介です! PySnooperというライブラリです。

※追記

想像以上にたくさんの方に読んでいただき、printデバッグなど他のデバッグ方法との比較について追記として文末に補足しました。釣りタイトルですみませんでした…🙇

f:id:ikedaosushi:20190428000712p:plain

何ができるライブラリか

一言で言うと「デバッグがめちゃくちゃ簡単にできるライブラリ」です。

github.com

例を見たほうがわかりやすいと思うので見てみましょう。

インストールは pip で行えます。

pip install pysnooper

aとbを二乗して足すだけの関数を書いてみます。デバッグしたい関数に @pysnooper.snoop() デコレータをつけるだけです。

example.py

import pysnooper

@pysnooper.snoop()
def plus_with_power(a, b):
    a = a * a
    b = b * b
    c = a + b

    return c

if __name__ == "__main__":
    result = plus_with_power(2, 3)
    print(result)

出力結果は次のようになります。

$ python example.py
Starting var:.. a = 2
Starting var:.. b = 3
08:29:18.463391 call         4 def plus_with_power(a, b):
08:29:18.464489 line         5     a = a * a
Modified var:.. a = 4
08:29:18.464552 line         6     b = b * b
Modified var:.. b = 9
08:29:18.464604 line         7     c = a + b
New var:....... c = 13
08:29:18.465016 line         9     return c
08:29:18.465074 return       9     return c
Return value:.. 13
13

「新しい変数が宣言されたとき」「変数が書き換わったとき」「関数が呼び出されたとき」「値が関数から返されたとき」などが表示されていると思います。 こんな感じで print をたくさん入れたり、 pdb などのデバッガーをセットアップする必要なく、簡単にデバッグを行うことができます。

他の機能

出力をファイルに書き出すことができます。

@pysnooper.snoop('/my/log/file.log')

出力にprefixをつけることができます。

@pysnooper.snoop(prefix='MyPrefix ')
# =>
# MyPrefix Starting var:.. a = 2
# MyPrefix Starting var:.. b = 3
# MyPrefix 08:37:55.602570 call         4 def plus_with_power(a, b):

出力する深さを制御できます。

@pysnooper.snoop(depth=2)

インスタンス変数などローカル変数以外の変数を見ることもできます。

@pysnooper.snoop(variables=('foo.bar',  'self.a'))

次のような感じで使います。

import pysnooper

class Plus:
    def __init__(self, a):
        self.a = a

    @pysnooper.snoop(variables=('self.a'))
    def plus(self, b):
        c = self.a + b

        return c

if __name__ == "__main__":
    result = Plus(2).plus(3)
    print(result)

出力結果

Starting var:.. b = 3
Starting var:.. self = <__main__.Plus object at 0x10e1270f0>
Starting var:.. self.a = 2 # <= が追加される
08:49:56.226036 call         8     def plus(self, b):
08:49:56.226733 line         9         c = self.a + b
New var:....... c = 5
08:49:56.226838 line        11         return c
08:49:56.226902 return      11         return c
Return value:.. 5

まとめ

楽々にデバッグできるツールPySnooperを紹介しました。コミット履歴を見ると2週間くらいで作った実験的なツールみたいですが、既にGitHubでは7000Star以上ついていて、みんなが求めていたものだったんだろうなと思いました。こういう日常的なちょっとした不満を解決できるツールは思いついたらどんどん作っていきたいですね。

追記

想像以上にたくさんの方に読んでいただいているので少し補足します。 まずタイトルの「もうprintデバッグはいらない」というのは「Never use print for debugging again」というPySnoozerの宣伝文句で、本記事は明確に print デバッグを否定するものではありません。

元記事も本記事もいろいろな人に読んでもらいたいため「 print デバッグはもういらない」と誇張して書いておりますが、コメントでも頂いている通り、 print デバッグの方が便利な点も当然たくさんありますので、他のツール選定と同様ユースケースによって使い分けるのが良い使い方だと思います。誇張した表現で気を悪くされた方がいたらすみませんでした🙇

それぞれのデバッグ方法との関係性ですが、 print デバッグだけでやるには大変( print 文をたくさん入れないといけない)だけど、breakpointを設定するのは面倒くさい、というような場面で使うことが想定されていると考えています。

Pythonには pdb というデバッガーモジュールがありますが、デバッガーツールをセットアップして実行するのは(めちゃくちゃ慣れている方でなければ)少し大変だと思うので、そんなときに便利なツールで、しっかりとデバッグしたい場合は pdb を使うべきだと思います。

docs.python.org