Pythonでvar_dump

PHPでコードを書いててちょっと動作確認したいというときにvar_dumpは本当に便利。元からあるデータ型だろうが、自作のクラスだろうがきれいに整形してすべてのデータを表示してくれる。

<?php
$a = array(1, 2, array("a", "b", "c"));
var_dump($a);
?>

# => array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  array(3) {
    [0]=>
    string(1) "a"
    [1]=>
    string(1) "b"
    [2]=>
    string(1) "c"
  }
}

自作クラスのインスタンスでもほぼ同じ形式で表示できる。さて、Pythonではどうかというと

from pprint import pprint
a = [1, 2, ["a", "b", "c"]]
pprint(a)
# => [1, 2, ['a', 'b', 'c']]

別に悪くない。ところが、自作クラスを表示するとこうだ

from pprint import pprint
class A(object):
  def __init__(self): self.num = 100
a = A()
pprint(a)
# => <__main__.A object at 0x0120F630>

Aのインスタンスであることしか分からない!なんてこった!
これまでは、インスタンスが持つ変数を表示させたいときは__str__メソッドを用意してインスタンスの文字列表現を指定していたのだが、大量の変数を持つクラスではあまりに面倒くさい。そこで、Python版var_dumpの出番だ。

元ネタはこちらのページ中段にあるdump関数。
What is a python equivalent of PHP's var_dump() - Stack Overflow

これでも悪くはないのだけど、インスタンス変数がlistやdictで、さらにその中に自作クラスのインスタンスが入っているような場合にちゃんと表示してくれないので、中身まで全部表示するようにしてみた。
※Python2.6, 3.1で動作確認
追記

  • メンバや要素に関数をいれている場合に表示できるようにした
# -*- coding: utf-8 -*-
"""
PHPのvar_dump的なもの
"""
from pprint import pprint
import types

def var_dump(obj):
  pprint(dump(obj))

def dump(obj):
  '''return a printable representation of an object for debugging'''
  newobj = obj
  if isinstance(obj, list):
    # リストの中身を表示できる形式にする
    newobj = []
    for item in obj:
      newobj.append(dump(item))
  elif isinstance(obj, tuple):
    # タプルの中身を表示できる形式にする
    temp = []
    for item in obj:
      temp.append(dump(item))
    newobj = tuple(temp)
  elif isinstance(obj, set):
    # セットの中身を表示できる形式にする
    temp = []
    for item in obj:
      # itemがclassの場合はdump()は辞書を返すが,辞書はsetで使用できないので文字列にする
      temp.append(str(dump(item)))
    newobj = set(temp)
  elif isinstance(obj, dict):
    # 辞書の中身(キー、値)を表示できる形式にする
    newobj = {}
    for key, value in obj.items():
      # keyがclassの場合はdump()はdictを返すが,dictはキーになれないので文字列にする
      newobj[str(dump(key))] = dump(value)
  elif isinstance(obj, types.FunctionType):
    # 関数を表示できる形式にする
    newobj = repr(obj)
  elif '__dict__' in dir(obj):
    # 新しい形式のクラス class Hoge(object)のインスタンスは__dict__を持っている
    newobj = obj.__dict__.copy()
    if ' object at ' in str(obj) and not '__type__' in newobj:
      newobj['__type__']=str(obj).replace(" object at ", " #").replace("__main__.", "")
    for attr in newobj:
      newobj[attr]=dump(newobj[attr])
  return newobj

それぞれのデータ型に対応しただけだが、以下のような場合でもすべてのデータを最後まで表示できるようになった。ただし、インスタンス変数に自分自身を割り当てるような再帰的な構造になっていると再帰しまくって落ちるので悪しからず。

class A(object):
  def __init__(self, num):
    self.num = num

a = A(5)
a.list = [A(3), A(4)]
a.dict = {"x": [A(3), A(5)], A(100): 100}
a.set = set([A(5), 100])
var_dump(a)

# => 
{'__type__': '<>',
 'dict': {'x': [{'__type__': '<>', 'num': 3},
                {'__type__': '<>', 'num': 5}],
          "{'num': 100, '__type__': '<>'}": 100},

 'list': [{'__type__': '<>', 'num': 3},
          {'__type__': '<>', 'num': 4}],
 'num': 5,
 'set': set(['100',
             "{'num': 5, '__type__': '<>'}"])}

このブログの横幅の関係で__type__の中身は省略。