printデバッグに最適! pythonで変数名を文字列にして、中の値と同時にprintする関数を実装


printデバッグをpythonで行う場合、以下のように書くことが多いのではないだろうか。

print("hoge = " + str(hoge))

しかしわざわざダブルクォーテーションで囲ったりstr()を用いたりするのは面倒である。
そこで、pythonインターフェースを開いたら、以下のようなchkprint関数を記述しておくと良い。

1. 実装

def chkprint(*args):
    flg = 1
    for obj in args:
        for k, v in globals().items():
            if id(v) == id(obj):
                target = k
                break
        if flg == 1:
            out = target+' = '+str(obj)
            flg = 0
        else:
            out += ', '+target+' = '+str(obj)            
    print(out)

この関数を一番最初に定義しておくだけ。

(※2017/06/07追記 : 上記の関数はグローバル変数にしか対応していません。shiracamusさんのコメントに書かれている関数がローカル変数にも対応しているので、ここに記載します)

from inspect import currentframe

def chkprint(*args):
    names = {id(v):k for k,v in currentframe().f_back.f_locals.items()}
    print(', '.join(names.get(id(arg),'???')+' = '+repr(arg) for arg in args))

2. 挙動

def chkprint(*args):
    flg = 1
    for obj in args:
        for k, v in globals().items():
            if id(v) == id(obj):
                target = k
                break
        if flg == 1:
            out = target+' = '+str(obj)
            flg = 0
        else:
            out += ', '+target+' = '+str(obj)            
    print(out)


if __name__=="__main__":
    a = 1
    lst = [3, 1, 4, 1, 5]
    chkprint(a)
    chkprint(lst)
    chkprint(a, lst)
実行結果
a = 1
lst = [3, 1, 4, 1, 5]
a = 1, lst = [3, 1, 4, 1, 5]

こんな感じになる。
長いコードを動かしている途中でprintデバッグをはさみたくなった時、このchkprint関数は手軽に「変数名」と「その変数の中身」を同時にprintしてくれるので、とても便利であると思われる。

3. 詳細

上記の関数で、一体何をやっているのかの説明。

for k, v in globals().items():
    if id(v) == id(obj):
        target = k
        break

この部分はすべて、変数obj"obj"という文字列に変換するためのものである。
globals()とは、グローバル変数の一覧を辞書として返す「組み込み関数」である。

>>> globals()
{ '__name__': '__main__', 'a': 1, 'lst': [3, 1, 4, 1, 5], '__package__': None, 
'__builtins__': <module 'builtins' (built-in)>, '__doc__': None, '__spec__': None}

この中でvidobjidと一致した場合には、その時のkの値が変数名の文字列、ということになる。
(id()を付けているのは、「中の値はobjと同じだけれども変数名はobjと異なるような変数」が拾われてしまうのを避けるためである)

参考URL