Javascript のハッシュマップはドット・ノーテーション ( dot notation )でアイテムにアクセスできますよね。でも 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() とかもオーバーライドしてバリデーションすればいいんじゃないですかね。他にも副作用があるかもしれません。