Defaultdict'in iç içe geçmiş defaultdict


129

Bir varsayılan diktenin aynı zamanda varsayılan diktenin de varsayılan olması için bir yol var mı? (yani sonsuz düzeyde özyinelemeli defaultdict?)

Yapabilmek istiyorum:

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

Yani yapabilirim x = defaultdict(defaultdict), ama bu sadece ikinci bir seviye:

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

Bunu yapabilecek tarifler var. Ancak, sadece normal defaultdict argümanları kullanılarak yapılabilir mi?

Unutmayın ki bu, sonsuz düzeyde özyinelemeli bir defaultdict'in nasıl yapılacağını sormaktadır, bu yüzden Python'dan farklıdır : defaultdict of defaultdict? , iki seviyeli bir varsayılan kararın nasıl yapılacağıydı.

Muhtemelen demet modelini kullanmayı bırakacağım , ama bunu nasıl yapacağımı bilmediğimi anladığımda ilgimi çekti.



2
Gerçekten değil ... nedenini belirtmek için soruya bilgi eklendi. Yine de bu yararlı bir soru.
Corley Brigman

Yanıtlar:


168

Keyfi sayıda düzey için:

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

Elbette bunu bir lambda ile de yapabilirsiniz, ancak lambdaları daha az okunaklı buluyorum. Her halükarda şöyle görünecektir:

rec_dd = lambda: defaultdict(rec_dd)

1
Gerçekten mükemmel bir örnek, teşekkürler. Lütfen, verilerin json'dan defaultdict'in defaultdict'e yüklenmesi durumuna genişletebilir misiniz?
David Belohrad

4
Bir not. Turşu yaparken bu kodu kullanmaya çalışıyorsanız lambdaişe yaramayacaktır.
Viacheslav Kondratiuk

167

Buradaki diğer cevaplar size defaultdict"sonsuz sayıda" içeren bir tanesinin nasıl yaratılacağını anlatıyor defaultdict, ancak ilk ihtiyacınızın olduğunu düşündüğüm, basitçe iki derinlikli bir ön tanıma sahip olmak olan şeyi ele almıyorlar.

Şunları arıyor olabilirsiniz:

defaultdict(lambda: defaultdict(dict))

Bu yapıyı tercih etmenizin nedenleri şunlardır:

  • Özyinelemeli çözümden daha açık ve bu nedenle okuyucu için muhtemelen daha anlaşılır.
  • Bu, "yaprağının" defaultdictsözlükten başka bir şey olmasını sağlar, örneğin: defaultdict(lambda: defaultdict(list))veyadefaultdict(lambda: defaultdict(set))

3
defaultdict (lambda: defaultdict (liste)) Doğru form?
Yuvaraj Loganathan

Hata, evet, lambdaform doğru - çünkü defaultdict(something)sözlüğe benzer bir nesne döndürüyor, ancak defaultdictçağrılabilir bir şey bekliyor! Teşekkür ederim!
Chris W.

4
Bu, başka bir sorunun olası bir kopyası olarak işaretlendi ... ama asıl sorum bu değildi. İki seviyeli bir defaultdict'in nasıl oluşturulacağını biliyordum; bilmediğim şey onu nasıl özyinelemeli yapacağımdı. Bu cevap için, aslında, benzer stackoverflow.com/questions/5029934/...
Corley Brigman

Lambda yaklaşımının bir dict(result)
dezavantajı

54

Bunu yapmak için harika bir numara var:

tree = lambda: defaultdict(tree)

Daha sonra xile oluşturabilirsiniz x = tree().


22

BrenBarn'ın çözümüne benzer, ancak değişkenin adını treeiki kez içermez , bu nedenle değişken sözlüğünde yapılan değişikliklerden sonra bile çalışır:

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

Sonra her yenisini xile oluşturabilirsiniz x = tree().


İçin defversiyonunda, biz eğer mevcut örnekleri durmasına kusur gelen veri yapısını korumak için işlev kapanışı kapsam kullanabilirsiniz treeadı ribaund olduğunu. Şöyle görünüyor:

from collections import defaultdict

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

4
Bunun hakkında düşünmem gerekecek (biraz daha karmaşık). ama bence amacınız, eğer x = tree () yaparsanız, ama sonra birisi daha sonra gelir ve tree = None olursa, bu hala çalışır ve bu olmaz mı?
Corley Brigman

11

Ayrıca, sonsuz iç içe geçmeyi destekleyen ve uygun şekilde biçimlendirilen daha fazla OOP tarzı uygulama öneririm repr.

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

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

Kullanımı:

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
Düzgün! Anahtar kelime argümanları ile bir dict oluşturmak için geçişini ekledim *argsve **kwargsonun gibi çalışmasına izin veriyor defaultdict. Bu aktarmak için kullanışlıdır NestedDefaultDictiçinejson.load
Ciprian Tomoiagă

0

burada özyinelemeli bir varsayılan dikteyi normal bir dikteye dönüştürmek için özyinelemeli bir işlev var

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

Bunu Andrew'un cevabına dayandırdım . Bir json'dan veya mevcut bir dikteden nester defaultdict'e veri yüklemek istiyorsanız şu örneğe bakın:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.