Pythonの辞書にドット・ノーテーションでアクセスする

Javascript のハッシュマップはドット・ノーテーションでアイテムにアクセスできますよね。でも Python の辞書ではそれができません。できたら便利だなーと考えていたらひらめいたので、 dict クラスを継承した dict2 クラスを作りました。

class dict2(dict): 
    def __init__(self, *args, **kwargs): 
        super().__init__(*args, **kwargs) 
        self.__dict__ = self 

コンストラクタ __init__() をオーバーライドして self.__dict__ に self を代入する行を加えました。ただそれだけです。

__dict__ はオブジェクトの書き込み可能なアトリビュートを格納するために使用される辞書またはその他のマッピングオブジェクトです。 self はまさに dict を継承したマッピングオブジェクトなので、アトリビュートを格納する __dict__ に self を代入すれば、ドット・ノーテーションでアトリビュートとして辞書のアイテムにアクセスできるようになるってわけです。

ピンとこなくても、とりあえず動作を見てみましょう。

まず IPython を起動したら、上の dict2 クラスのコードをコピペします。必ずしも IPython である必要はありませんが、解説に便利なので使います。

$ ipython
Python 3.5.2

In [1]: # ここにコピペして[Enter]

こんな感じで、 Javascript のようにアトリビュートにキー名を書くとバリューが返ります。

In [2]: d2 = dict2({'abc': 123})
In [3]: d2.abc
Out[3]: 123

In [4]: d2.update({'abc': 456})
In [5]: d2.abc
Out[5]: 456

ネストすればチェインもできます。

In [6]: d2.update({'ijk': dict2({'xyz': 789})})
In [7]: d2
Out[7]: {'abc': 456, 'ijk': {'xyz': 789}}

In [8]: d2.ijk.xyz
Out[8]: 789

さらに、ドット・ノーテーションで辞書のアイテムを追加することもできます。

In [9]: d2.uvw = 'foobar'
In [10]: d2['uvw']
Out[10]: 'foobar'

もちろん、ドット・ノーテーションで辞書のアイテムを更新できます。

In [11]: d2.abc = 321
In [12]: d2['abc']
Out[12]: 321

万能すぎる…

なんだろう、このダクトテープとか結束バンドを使った修理に似た感覚は。

注意点としては、キーに予約語とか演算子の記号とか数値リテラルとか変数名に使えない文字列が入ると、辞書のキーとしては問題ないですが、ドット・ノーテーションだとシンタックスエラーになります。あと、すでにあるアトリビュート名とキーが衝突すると上書きされるので気をつけましょう。そういう諸々をエラーにしたいときは、 __init__() に加えて __setitem__() とか update() とかもオーバーライドしてバリデーションすればいいんじゃないですかね。他にも副作用があるかもしれません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*