defaultdictのネストされたdefaultdict


129

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を実行する方法でした。

結局はバンチパターンを使うだけになるかもしれませんが、どうすればいいのかわからなかったので気になりました。



2
本当に...理由を示すために質問に情報を追加しました。それは有用な質問ですが。
Corley Brigman 2017年

回答:


168

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

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)

1
確かに、完璧な例です、ありがとう。データをjsonからdefaultdictのdefaultdictにロードする場合に拡張してください。
David Belohrad 2017年

4
1つのメモ。酸洗い中にこのコードを使用しようとするlambdaと、機能しません。
Viacheslav Kondratiuk 2017

167

ここでの他の答えdefaultdictは、「無限に多くの」を含むを作成する方法を示していますdefaultdictが、それらは、2つの深さのdefaultdictを持つことであった最初の必要性であったと私が考えるものに対処できません。

あなたが探していた可能性があります:

defaultdict(lambda: defaultdict(dict))

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

  • これは、再帰的ソリューションよりも明確であるため、おそらく読者にとって理解しやすいでしょう。
  • これは、の「リーフ」ができdefaultdict辞書以外のもの、例えばされることを,: defaultdict(lambda: defaultdict(list))defaultdict(lambda: defaultdict(set))

3
defaultdict(lambda:defaultdict(list))正しい形式?
Yuvaraj Loganathan 2015

おっと、はい、lambdaフォームは正しいです- defaultdict(something)は辞書のようなオブジェクトを返しますがdefaultdict、呼び出し可能を期待しているからです!ありがとうございました!
Chris W.

4
これは別の質問の重複の可能性があるとマークされました...しかし、それは私の元の質問ではありませんでした。2レベルのdefaultdictを作成する方法を知っていました。私が知らなかったのは、それを再帰的にする方法でした。この答えは、実際には、stackoverflow.com
questions / 5029934 /…に

ラムダアプローチのdict(result)
欠点の

54

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

tree = lambda: defaultdict(tree)

次に、あなたを作成することができますxx = tree()


22

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

tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a))))

その後、それぞれの新しいを作成することができますxx = tree()


defバージョンの場合、関数クロージャースコープを使用して、tree名前が再バインドされると既存のインスタンスが機能しなくなるという欠陥からデータ構造を保護できます。次のようになります。

from collections import defaultdict

def tree():
    def the_tree():
        return defaultdict(the_tree)
    return the_tree()

4
これについて考えなければなりません(少し複雑です)。しかし、あなたのポイントは、x = tree()を実行した場合、誰かが後で来てtree = Noneを実行すると、これはまだ機能し、それは機能しないということです。
Corley Brigman 2013年

11

また、適切にフォーマットされているだけでなく、無限のネストもサポートする、よりOOPスタイルの実装を提案しますrepr

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': {}}

1
きちんと!私はのパススルーを追加*argsし、**kwargsどのように機能することを可能にするdefaultdict、すなわちキーワード引数を持つ辞書を作成するには、。これは通過NestedDefaultDictするのに役立ちますjson.load
CiprianTomoiagă19年

0

これは、再帰的なデフォルトの辞書を通常の辞書に変換するための再帰関数です

def defdict_to_dict(defdict, finaldict):
    # pass in an empty dict for finaldict
    for k, v in defdict.items():
        if isinstance(v, defaultdict):
            # new level created and that is the new value
            finaldict[k] = defdict_to_dict(v, {})
        else:
            finaldict[k] = v
    return finaldict

defdict_to_dict(my_rec_default_dict, {})

0

私はここにアンドリューの答えを基にしています。jsonまたは既存のdictからnester defaultdictにデータをロードする場合は、次の例を参照してください。

def nested_defaultdict(existing=None, **kwargs):
    if existing is None:
        existing = {}
    if not isinstance(existing, dict):
        return existing
    existing = {key: nested_defaultdict(val) for key, val in existing.items()}
    return defaultdict(nested_defaultdict, existing, **kwargs)

https://gist.github.com/nucklehead/2d29628bb49115f3c30e78c071207775

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.