1. ホーム
  2. python

pdbでPythonをデバッグするとき、各変数を指定せずにすべての変数の値を表示するにはどうしたらいいですか?

2023-09-18 05:47:07

質問

Pythonスクリプトのデバッグに pdb を使っていますが、マニュアルによると p 変数 コマンドを使えば、ある時点の指定された変数の値を表示することができると書いてあります。しかし、もし私がたくさんの変数、例えば20個の変数を持っていて、それらすべての値を追跡したい場合はどうしたらよいでしょうか?それぞれの変数を手動で指定することなく、すべての変数を表示するにはどうしたらよいでしょうか?たとえば、こんなスクリプトがあります。

a = 1
b = 2
c = 3

pdbでデバッグして、全部を表示するには p a, b, c のようにします。

$ python -m pdb test.py 
> /media/test.py(1)<module>()
-> a = 1
(Pdb) n
> /media/test.py(2)<module>()
-> b = 2
(Pdb) n
> /media/test.py(3)<module>()
(Pdb) n
--Return--
> /media/test.py(3)<module>()->None
-> c = 3
(Pdb) p a, b, c
(1, 2, 3)
(Pdb) 

しかし、私は手動で各変数を指定する必要があります。一つ一つの変数を指定せずに、全ての変数を一度に表示する方法はありませんか?

どのように解決するのですか?

pdbは完全な機能を持ったPythonシェルなので、任意のコマンドを実行することができます。

locals()globals() は、スコープ内のすべての変数をその値とともに表示します。

を使うことができます。 dir() を使うことができます。

Pythonで変数を宣言すると、適宜ローカルやグローバルに入れられるので、自分で定義した変数と他の理由で自分のスコープに入ったものを区別する方法はありません。

dir()を使う場合、興味のある変数はそのリストの先頭か末尾にあることが多いでしょう。 もしあなたがキーと値のペアを取得したいのであれば

locals() のフィルタリング は以下のような感じになるかもしれません。

>>> x = 10
>>> y = 20
>>> {k: v for k,v in locals().iteritems() if '__' not in k and 'pdb' not in k}
{'y': 20, 'x': 10}

locals()が本当に混乱している場合、もう少し手強いものが必要でしょう。次の関数をpythonpath上のモジュールに置き、デバッグセッション中にそれをインポートすることができます。

def debug_nice(locals_dict, keys=[]):
    globals()['types'] = `__import__`('types')
    exclude_keys = ['copyright', 'credits', 'False', 
                    'True', 'None', 'Ellipsis', 'quit']
    exclude_valuetypes = [types.BuiltinFunctionType,
                          types.BuiltinMethodType,
                          types.ModuleType,
                          types.TypeType,
                          types.FunctionType]
    return {k: v for k,v in locals_dict.iteritems() if not
               (k in keys or
                k in exclude_keys or
                type(v) in exclude_valuetypes) and
               k[0] != '_'}

にセッションの例を追加しました。 ペーストビン

これでは見逃すケースがいくつかあります。また、型を渡すことができるように拡張したいかもしれません。 しかし、定義した変数以外のほとんどのものをフィルタリングできるようにする必要があります。

dir()

もし最後の20個の値だけが欲しいのであれば、次のような出力が得られます。 >>> p var1 var2 ... varn のような出力が得られるのであれば、dir()[-20:]のようにスライスしたほうがよいでしょう。 では変数と値の関係を簡単に見ることはできません。 例: "Did I declare foo before or after bar?"

もし、その関係を見たいのであれば、以下のような方法を試してみてください。これは、変数がdir()の最後にあると仮定しています。 変数が先頭にある場合は、別の方法でスライスすることができます。変数が連続していない場合、これはうまく動作しないでしょう。

>>> zip(dir(), [eval(var) for var in dir()])[-4:]
[('a', 10), ('var', 'var'), ('x', 30), ('y', 50)]