🔖

PythonのSimpleNamespaceを拡張して辞書的な値の取得も可能にする

に公開

Pythonの標準ライブラリにはtype.SimpleNamespaceというものがあります[1]SimpleNamespaceを利用すれば,辞書オブジェクトの値に属性アクセスすることが可能になります。例えば,以下のような辞書を考えます。

test_dict1 = {
    'key': 'value'
}

test_dictのkeyキーの値にアクセスする場合,test_dict['key']のようにする必要があります。しかし,以下のようにtest_dictSimpleNamespaceを利用すればtest_dict1.keyのような値のアクセスが可能です。

from types import SimpleNamespace
test_namespace1 = SimpleNamespace(**test_dict1)
print(test_namespace1.key) # value

一方で,辞書をSimpleNamespaceに変換してしまうと,変換後のオブジェクトでは辞書的な値のアクセスができなくなります。

from types import SimpleNamespace
test_namespace1 = SimpleNamespace(**test_dict1)
print(test_namespace1['key']) # これは不可能

したがって,例えば数字が先頭にくるようなキーを持つ辞書をSimpleNamespaceに変換すると,そのキーの値にアクセスすることができなくなります。

from types import SimpleNamespace
test_dict2 = {
    'key': 'value'
    '1key': '1value'
}
test_namespace2 = SimpleNamespace(**test_dict2)
test_namespace2.1key # エラーになる

属性アクセスと辞書的アクセスを両立させるためには,SimpleNamespaceを拡張して,辞書的アクセスを可能にする必要があります。具体的には以下のように実装できます。

class DictLineNamespace(SimpleNamespace):
    def __getitem__(self, key):
        return getattr(self, key)

    def __setitem__(self, key, value):
        return setattr(self, key, value)

test_namespace3 = SimpleNamespace(**test_dict2)

# test_namespace3には辞書のようなアクセスが可能
print(test_namespace3['1key']) # 1value

# SimpleNamespaceを継承しているので属性アクセスも可能
print(test_namespace3.key) # value

__getitem__および__setitem__は、[]を使った辞書的アクセスを処理する特殊メソッドです。__getitem__は値を取得する場合に呼び出され,__setitem__は値をセットする場合に呼び出されます。これらのメソッドが呼び出されたときに,getattrを利用してSimpleNamespaceの値に動的にアクセスして処理を行っています。

このようにして,SimpleNamespaceでも辞書的なアクセスが可能になります。

脚注
  1. https://docs.python.org/3/library/types.html ↩︎

Discussion