0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonのdict(っぽいもの)にドットでアクセスするいろんな方法

Posted at

はじめに

Pythonの辞書型を使っていて「JavaScriptのオブジェクトみたいにドット.でアクセスしたい!」と思うことってありませんか?
data['key'] のように文字列で書くのって地味にめんどくさいと思うんです。

なにか方法ないのかなと思って、調べてみるといくつか方法があるようだったのでまとめてみました。

クラス:辞書を継承する

調べるとクラスを継承する形のものがいくつか見つかりました。
(調べて見つかったものを改変しています。)

class DotDict1(dict):
    def __getattr__(self, item):
        return self[item]

    def __setattr__(self, key, value):
        self[key] = value

    def __delattr__(self, item):
        del self[item]
class DotDict2(dict):
  def __getattr__(self, key):
    return self.__getitem__(key)
  
  def __setattr__(self, key, value):
    self.__setitem__(key, value)

  def __delattr__(self, key):
    self.__delitem__(key)
class DotDict3(dict):
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__
class DotDict4(dict):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__dict__ = self

この中で DotDict1, DotDict2, DotDict3 は同じです。 どれもドットでアクセス(属性にアクセス)したとき、['key'] でのアクセス(辞書へのアクセス)と同じ処理をする、という実装をしています。それぞれ書き方が異なっているだけで機能的には同じになります。

しかし、DotDict4 だけは若干異なり、ちょっと扱いを気をつける必要があります。具体的には、以下のようにkeys, values などといった辞書にもとからあるメソッドへのアクセスに注意が必要です。

d = DotDict4()
d.keys = 1
print(d.keys) # 1
print(d['keys']) # 1
d.keys() # TypeError: 'int' object is not callable

何が起きているかというと、辞書の属性値としてkeys を設定したつもりが、辞書のkeysメソッドを上書きしており、keysメソッドが使用できなくなっています。このように既存のメソッドに誤って値を設定できてしまうので、注意が必要です。

他の DotDict1, DotDict2, DotDict3 であれば以下のように上書きせずに設定ができます。
ただ、keysなどはメソッドと名称が重複してしまっているので、ドットでのアクセスはできません。

d = DotDict1()
d.keys = 1
print(d.keys) # <built-in method keys of DotDict1 object>
print(d['keys']) # 1
print(d.keys()) # dict_keys(['keys'])

SimpleNamespace

typesSimpleNamespace を使う方法です。
もとの辞書方式でのアクセスはできませんが、ドットでアクセスすることができます。またこの方法の場合、VSCode上で設定した値の補完機能が働きます。
通常のdictとほとんど変わらずシンプルに書けますし、お手軽なのではないかと思います。

ただ、変数名として許可されていないもの(数字始まりや+等の記号など)は使えないため、そこだけは注意が必要でしょうか。

from types import SimpleNamespace

d = {'name': 'Alice', 'age': 30}
ns = SimpleNamespace(**d)
# ns = SimpleNamespace(name='Alice', age=30) # 同じ

# 追加や削除も可能
ns.abc = 123
del ns.abc

# 辞書でのアクセスはできない
# ns['name'] # error

namedtuple

collectionsnamedtuple を使う方法です。この方法でもドットでアクセスが可能です。
ただ、最初に定義した変数しか設定できませんし、インスタンスを作成したあとに変更もできません。
逆に言えば変更することができないので、動的に変更することがないとわかっており、定義を明確にして誤って変更してしまうことを防ぎたいのであれば、namedtuple がいいのではないかと思います。

また、VSCodeで補完機能が働くことや、変数名として許可されているもののみが使用できることはSimpleNamespaceと同様になります。

from collections import namedtuple

# namedtuple を定義
Person = namedtuple('Person', 'name age')

# インスタンスを作成
person = Person(name='Bob', age=25)

# タプルなので変更不可
person.name = 'Alice' # AttributeError: can't set attribute
person.abc = 123 # AttributeError: 'Person' object has no attribute 'abc'

まとめ

辞書っぽいものにドットでアクセスするいくつかの方法でした。
個人的には、SimpleNamespace がお手軽で使いやすいのではないかなと思います。

参考

クラス

SimpleNamespace

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?