obj.foo
dict キーに ではなく としてアクセスする方が便利だと思うのでobj['foo']
、次のスニペットを書きました:
class AttributeDict(dict):
def __getattr__(self, attr):
return self[attr]
def __setattr__(self, attr, value):
self[attr] = value
しかし、Python がこの機能を提供していないのには何らかの理由があるはずです。この方法で dict キーにアクセスする場合の注意点や落とし穴は何でしょうか?
ベストアンサー1
アップデート - 2020
この質問が出されたのは約 10 年前ですが、それ以来 Python 自体にはかなりの変化がありました。
私の最初の回答のアプローチは、いくつかのケース(例えば、古いバージョンのPythonに固執しているレガシープロジェクトや、非常に動的な文字列キーを持つ辞書を本当に処理する必要がある場合)では依然として有効ですが、一般的にはデータクラスPython 3.7 で導入された は、 のユースケースの大部分に対する明白な/正しいソリューションですAttrDict
。
元の回答
これを行う最善の方法は次のとおりです。
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
良い点:
- 実際に機能します!
- 辞書クラスのメソッドはシャドウ化されません (たとえば、
.keys()
問題なく動作します。もちろん、何らかの値を割り当てない限り、以下を参照してください) - 属性とアイテムは常に同期されます
- 存在しないキーを属性としてアクセスしようとすると、
AttributeError
代わりに次のエラーが発生します。KeyError
- [Tab]オートコンプリートをサポート(例: jupyter および ipython)
短所:
- のような方法は、入力データによって上書きされるとうまく機能しなく
.keys()
なります。 - 原因メモリーリークPython < 2.7.4 / Python3 < 3.2.3の場合
- Pylintはバナナになり
E1123(unexpected-keyword-arg)
、E1103(maybe-no-member)
- 初心者にとっては、それは純粋な魔法のように思えます。
これがどのように機能するかについての簡単な説明
- すべての Python オブジェクトは、 という名前の辞書に属性を内部的に格納します
__dict__
。 __dict__
内部辞書が「単なる単純な辞書」である必要はないため、dict()
内部辞書に の任意のサブクラスを割り当てることができます。- 私たちの場合、
AttrDict()
インスタンス化しているインスタンスを単に割り当てるだけです ( の場合と同様__init__
)。 super()
のメソッドを呼び出すことで、その関数がすべての辞書のインスタンス化コード__init__()
を呼び出すため、それが (すでに) 辞書とまったく同じように動作することを確認しました。
Pythonがこの機能を提供しない理由の1つは
「cons」リストに記載されているように、これは格納されたキーの名前空間 (任意のデータや信頼できないデータから取得される可能性があります) と組み込みの dict メソッド属性の名前空間を組み合わせます。例:
d = AttrDict()
d.update({'items':["jacket", "necktie", "trousers"]})
for k, v in d.items(): # TypeError: 'list' object is not callable
print "Never reached!"