Pythonで変数名と変数の値をまとめてprintする


地味に面倒な print を拡張する

Pythonでprintを行う際、変数名を一緒に記述しないと出力結果が数字だらけになってコード検索もできないため、以下のように記述する人は多いと思う。

one_two_three_list = [1, 2, 3]
print('one_two_three_list = ' + str(one_two_three_list))

# 出力結果: one_two_three_list = [1, 2, 3]

しかしこの方法だと記述量が多くなってしまうため、どうせ後で使わないから数値だけ表示すればいいや、となってしまう。(経験則)

そこで、print_関数を定義しておくことで普段のprintに加えて変数名も表示することができる。

print_(one_two_three_list)

# 出力結果
# one_two_three_list = [1, 2, 3]

加えて、機械学習などで .shape を頻繁に用いる人にとって、 print(X_train.shape) などとしたときに数値だけ表示されるため、目的の値を探すとき地味に時間と労力を使ってしまい、それらが蓄積すると厄介。

ここでも次のような print_shape 関数を用いることで、記述量を減らしつつ見やすい出力結果を得ることができる。

print_shape(X_train, y_train)

# 出力結果
# X_train_shape = (1500, 220500, 1)
# y_train_shape = (1500, 50)

そのために、次のコードをプログラムの最初にコピペする。

from inspect import currentframe

# 変数の名前と値をまとめて表示
def print_(*args):
    names = {id(v): k for k, v in currentframe().f_back.f_locals.items()}
    print('\n'.join([names.get(id(arg), '???') + ' = ' + repr(arg) for arg in args]))

# 変数の名前と形状をまとめて表示
def print_shape(*args):
    names = {id(v): k for k, v in currentframe().f_back.f_locals.items()}
    print('\n'.join([names.get(id(arg), '???') + '_shape = ' + repr(arg.shape) for arg in args]))

google colabのようにデバッグ機能が不十分な環境でも、機械学習系のエラーが起きた際にはvars()type()print_shape()を用いるとほとんど解決すると思う。

動作としては、namesで、変数のidと変数名の文字列型を紐付けて一覧表にし、printで変数のidを取り出した後、namesにある一覧表から変数名の文字列型を取得して出力する、という流れ。

ちなみに自分は最初、「たかが変数名を表示するためになんでこんな面倒なことをしないといけないんだ」と思ったが、idが変数と変数の値(オブジェクト)の橋渡しをしているということを知ってからは納得できた。

id はコンピュータの仕組みを知る上でめちゃくちゃ重要なので、ぜひこの記事もご覧ください。メモリを食いがちな方の一助になると思います。

追記

radiol 様に教えて頂いたicecreamというライブラリを用いる方法が、簡単かつ汎用性が高かったので共有します。

インストールはコマンドラインにて

pip install icecream

Pythonでの使用例

from icecream import ic
import numpy as np

# 「print」をすべて「ic」に変えるだけ

# 配列のshape
numpy_array = np.zeros((10, 10))
ic(numpy_array.shape)

# 辞書のkey
fruit_dic = {'apple':120, 'orange':60, 'lemon':80}
ic(fruit_dic)
ic(fruit_dic['orange'])

# 関数名
def hello_function(name):
    return 'hello, ' + name
ic(hello_function('太郎'))

出力結果

ic| numpy_array.shape: (10, 10)
ic| fruit_dic: {'apple': 120, 'lemon': 80, 'orange': 60}
ic| fruit_dic['orange']: 60
ic| hello_function('太郎'): 'hello, 太郎'

参考