ディレクトリ構造チックにネストした辞書を作れるPathDictを見つけた話


これは備忘録です。
何かPythonでオブジェクトを管理する処理を書きたいときに、ディレクトリ構造みたいに配置できたらなと思うことが往々にしてあります。
なにかいいライブラリないかなーと探していると3秒で出てきました。Python dict path like で出てきました。

さてインストールして使ってみますか。

pip install path-dict

インポート

from path_dict import PathDict
pd = PathDict()

使ってみる

user = {
	"name": "Joe",
	"age": 22,
	"hobbies": ["Playing football", "Podcasts"],
	"friends": {
		"Sue": {"age": 30},
		"Ben": {"age": 35},
	}
}
joe = PathDict(user)

print(joe["name"])
# joe
print(joe["friends","Sue"]) # 検索先がフォルダだとPathDictになって返す
# PathDict({
#   "age": 30
# })

事前に親ディレクトリまでのパスを作らなくても作ってくれる

pd = PathDict()
pd["a", "b", "c"] = 1
print(pd["a", "b", "c"])
# 1

print(pd)
# PathDict({
#   "a": {
#     "b": {
#       "c": 1
#     }
#   }
# })

詰まったこと

基本的な使い方に関しては公式ドキュメントが非常にわかりやすく、フィルター機能 も便利だが肝心なところで躓いたのでメモ。

ファイルパスを利用して登録したい。

パスの文字列をsplitして入れてみるとエラー
(2022/3/26 追記) 本家にpull requestが通りましたのでtuple型に変換しなくも処理できるようになります。

from path_dict import PathDict
pd = PathDict()
path = "path/to/file"
obj = 10
pd[path.split("/")] = obj
# TypeError: unhashable type: 'list'

どうやら公式実装の __ getitem __ メソッドがtuple型しか受け入れないようです。

path_dict/__init__.py
...
	def __getitem__(self, path) -> Any | PathDict:
		""" Subscript for <PathDict>.get_path() """
		# If PathDict["key1"], then path="key1"
		# PathDict["key1", "key2"], then path=tuple("key1", "key2")
		# We want path to be a list in any case
		path = list(path) if isinstance(path, tuple) else [path]
		return self.get_path(path)
...

ですからこのようにしていちいちtupleに型変換しないといけません。

from path_dict import PathDict
pd = PathDict()
path = "path/to/file"
obj = 10
pd[tuple(path.split("/"))] = obj # OK

ちょっと面倒くさいですね。修正してプルリクエストしておきます。

注意事項

個人的にこれは気を付けないと危険だと思った仕様

存在しないPathを指定してもエラーを吐かない

存在しないパスを指定すると None を返します。

from path_dict import PathDict
pd = PathDict()
print(pd["a","b"])
# None

つまり事実上None型をValueとして登録できません。しかしここでエラーを吐いてしまうと辞書がそのパスを持っているか調べるメソッドがありません。Issue案件です。
...
(2022/3/26 追記) __contains__ メソッドの実装をしプルリクエストを本家に送りました。pathの有効性のチェックが可能になります。

from path_dict import PathDIct
pd = PathDict()
pd["a","b"] = 1
print(["a"] in pd) # True
print(["a","b"] in pd) # True
print(["a","c"] in pd) # False

まとめ

  • Pythonの内部でディレクトリ構造みたいにオブジェクトを登録できるライブラリを見つけたよ。
  • tuple以外受け容れない問題があるよ
  • キーが存在しないときは None を返しエラーを吐かないので気を付けてね。

Issueとプルリクエストしよ。