Keep on moving

あんまりまとまってないことを書きますよ

辞書にオブジェクトっぽくアクセスする

はじめに

pythonでは2.6以降ならばjsonライブラリを使ってjsonを簡単に使えて便利です。
jsonライブラリを使うと、json文字列をparseして辞書で取得することができるのですが、
jsonの階層が深いとオブジェクトっぽくアクセスできないかなーと思うときがあります。
既に同じようなことをやってる人がいそうですが、時間があったので軽く実装してみました。

ソース

class DictAccessLikeObj(dict):
    """
    >>> DictAccessLikeObj({"foo":"hoge"}).foo
    'hoge'
    >>> DictAccessLikeObj({u"foo":[{u"bar":u"baz"}]}).foo[0].bar
    u'baz'
    >>> DictAccessLikeObj({u"foo":[[1,2,3]]}).foo[0][2]
    3
    """
    def __getattr__(self, key):
        if key in self:
            val = self[key]
            if isinstance(val, dict):
                return self.__class__(val)
            elif isinstance(val, list):
                return [self.__class__(v) if isinstance(v, dict) else v  for v in val]
            else:
                return val
        else:
            raise AttributeError, key

使い方

    import json
    test = DictAccessLikeObj(json.loads('''
    {"states" :
      [
           {"foo":"hoge"},
           123,
           {"baz":"piyo"}
      ]
    }
    '''))
    print(test.states)
    # => [{u'foo': u'hoge'}, 123, {u'baz': u'piyo'}]
    print(test.states[0])
    # => {u'foo': u'hoge'}
    print(test.states[2].baz)
    # => piyo

まとめ

うまいこと辞書をラップしてやることで実現できました。
お分かりだと思いますが、辞書としてアクセスしたほうが動作は早いです。
こんなこともできるってことが分かったのが今回の収穫ですね。
__getattr__は初めて使ったのですが、使い方が分かれば、抽象的な処理を行わせるときの強力な道具になりそうです。

追記(2011/6/22 AM1:00)

@pokarim さんからtwitterでコメントがありました。

import json

class D(dict):
  __getattr__ = dict.__getitem__ 

test = json.loads('{"foo":[{"a":1},2]}', object_hook=D)
print(test.foo[0].a)
=> 1

おぉー!こんなに簡単にかけるとは思わなかった。ありがとうございました。