Pythonのディクショナリに属性とアクセスするには

ディクショナリ(dict)に「data.attr」というようにアクセスしたくなりませんか?

その方法を探していたら見つかったので、紹介しておきたいと思います。
Python tricks: accessing dictionary items as object attributes

やりたいこと

下記のディクショナリがあった場合に、mydict.key1とアクセスしたい場合どうすればよいかということです。

mydict = dict(key1=1, key2=dict(key3=3, key4=4))

mydict['key1']  # OKだが、指定が面倒
mydict.key1     # エラー。こういうアクセスがしたい

解決策1 ビューを作る

下記のようなビュークラスを作ります。

class objectview(object):
    def __init__(self, d):
        self.__dict__ = d

これで、下記のように属性アクセスできるようになります。

mydict = dict(key1=1, key2=dict(key3=3, key4=4))
mydict_view = objectview(mydict)

mydict_view.key1   #1が返る OK
mydict_view.keys() #エラー。mydict_viewはディクショナリではない
mydict_view.__dict__.keys()  #これならOK

ですが、mydict_viewはディクショナリではないので、通常のディクショナリのI/Fを持っていないのでkeys()やitems()が呼び出せません。代わりに_dict_属性経由で、使い勝手がよくありません。

解決先2 ディクショナリを継承して拡張

下記のようなディクショナリのサブクラスを作ります。

class objdict(dict):
    def __getattr__(self, name):
        #print("__getattr__", name)
        if name in self:
            return self[name]
        else:
            raise AttributeError("No such attribute: " + name)

    def __setattr__(self, name, value):
        #print("__setattr__", name)
        self[name] = value

    def __delattr__(self, name):
        #print("__delattr__", name)
        if name in self:
            del self[name]
        else:
            raise AttributeError("No such attribute: " + name)

ディクショナリを継承しているので、解決策1と違って、keys()やitems()も使えます。

mydict_obj = objdict(key1=1, key2=objdict(key3=3, key4=4))
mydict_obj.key2.key3 # 3が返る
mydict.items()       # dict_items([('key1', 1), ('key2', {'key3': 3, 'key4': 4})])が返る

では、解決策2のobjdictを使えば何でも解決かというと違います。

既存のディクショナリをもとにobjdictを作ると、2階層目以降は本家のdictのままなので、属性アクセスできません。階層化を考えると、深い階層のディクショナリを作るところからobjdictで使っておく必要あります。

# この例は2階層目以降は通常のdictになっている
mydict = dict(key1=1, key2=dict(key3=3, key4=4))
mydict_obj = objdict(mydict)
mydict_obj.key2       # これはOK
mydict_obj.key2.key3  # これはエラー

# 2階層目もobjdictで作っていればOK
mydict_obj = objdict(key1=1, key2=objdict(key3=3, key4=4))
mydict_obj.key2.key3  # これでもOK

使い分け

解決策1のobjectviewと解決策2のobjdictの使い分けについて、Python tricks: accessing dictionary items as object attributesのサイトを読むと、

  • 既存のディクショナリがあり、それに属性アクセスするなら、▶解決策1のobjectview
  • 新しくディクショナリを作っていくなら、▶解決策2のobjdict

だと言っています。

解決策1のobjectviewは、dictに代入するだけで、作成は非常に軽量なので、objectview(mydict).key1のようにラップしてすぐ使えます。なので、既存のディクショナリがあって、それにアクセスしたいのであれば、新規オブジェクトを作るコストがもったいないので、こっちがよいということでしょう。どのみち、objectviewでも2階層目以降は属性アクセスできないので、取り出して再度objectviewでラップする必要があることに注意してください。

解決策2のobjdictは、ディクショナリの全機能を継承しているので、自分で新たにディクショナリを作っていくのであれば、本家のdictを作っていくより、objdictで作っていけばよいので、迷うことなくobjdictでオブジェクト作っていくとよいです。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする