web-dev-qa-db-ja.com

defaultdictのネストされたdefaultdict

Defaultdictをdefaultdictのデフォルトにもする方法はありますか? (つまり、無限レベルの再帰的なdefaultdict?)

私ができるようにしたい:

_x = defaultdict(...stuff...)
x[0][1][0]
{}
_

したがって、x = defaultdict(defaultdict)を実行できますが、それは第2レベルにすぎません。

_x[0]
{}
x[0][0]
KeyError: 0
_

これを行うことができるレシピがあります。しかし、通常のdefaultdict引数を使用するだけで実行できますか?

これは、無限レベルの再帰的なdefaultdictの実行方法を尋ねているため、Python:defaultdictのdefaultdict?とは異なります。これは、2レベルのdefaultdictを実行する方法でした。

おそらくbunchパターンを使用することになりますが、これを行う方法がわからないことに気付いたとき、興味を持ちました。

99
Corley Brigman

任意の数のレベルの場合:

def rec_dd():
    return defaultdict(rec_dd)

>>> x = rec_dd()
>>> x['a']['b']['c']['d']
defaultdict(<function rec_dd at 0x7f0dcef81500>, {})
>>> print json.dumps(x)
{"a": {"b": {"c": {"d": {}}}}}

もちろん、ラムダを使用してこれを行うこともできますが、ラムダは読みにくいと思います。いずれにしても、次のようになります。

rec_dd = lambda: defaultdict(rec_dd)
136
Andrew Clark

ここでの他の回答は、「無限に多く」のdefaultdictを含むdefaultdictを作成する方法を示していますが、2つだけを持っていることが最初のニーズだったと思うものに対処できません-depth defaultdict。

あなたが探しているかもしれません:

_defaultdict(lambda: defaultdict(dict))
_

この構成を好む理由は次のとおりです。

  • 再帰的なソリューションよりも明示的であるため、読者にとって理解しやすい可能性があります。
  • これにより、defaultdictの「リーフ」を辞書以外のものにすることができます。例:defaultdict(lambda: defaultdict(list))またはdefaultdict(lambda: defaultdict(set))
131
Chris W.

それを行うための気の利いたトリックがあります:

_tree = lambda: defaultdict(tree)
_

その後、x = tree()xを作成できます。

41
BrenBarn

BrenBarnのソリューションに似ていますが、変数treeの名前が2回含まれていないため、変数辞書を変更した後でも機能します。

_tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a))))
_

その後、x = tree()を使用して新しいxをそれぞれ作成できます。


defバージョンでは、関数閉鎖スコープを使用して、tree名が再バインドされた場合に既存のインスタンスが動作を停止するという欠陥からデータ構造を保護できます。次のようになります。

_from collections import defaultdict

def tree():
    def the_tree():
        return defaultdict(the_tree)
    return the_tree()
_
19
pts

また、適切にフォーマットされたreprと同様に、無限のネストをサポートする、よりOOPスタイルの実装を提案します。

class NestedDefaultDict(defaultdict):
    def __init__(self, *args, **kwargs):
        super(NestedDefaultDict, self).__init__(NestedDefaultDict, *args, **kwargs)

    def __repr__(self):
        return repr(dict(self))

使用法:

my_dict = NestedDefaultDict()
my_dict['a']['b'] = 1
my_dict['a']['c']['d'] = 2
my_dict['b']

print(my_dict)  # {'a': {'b': 1, 'c': {'d': 2}}, 'b': {}}
2