Python: defaultdict varsayılanı?


323

defaultdict(defaultdict(int))Aşağıdaki kodun çalışması için bir sahip olmanın bir yolu var mı?

for x in stuff:
    d[x.a][x.b] += x.c_int

dx.ave x.belemanlara bağlı olarak geçici olarak inşa edilmesi gerekir .

Kullanabilirim:

for x in stuff:
    d[x.a,x.b] += x.c_int

ama sonra kullanamazdım:

d.keys()
d[x.a].keys()

6
Benzer soruya bakın Python'da iç içe sözlükleri uygulamanın en iyi yolu nedir? . Vikipedi'nin Autovivification ile ilgili makalesinde muhtemelen bazı yararlı bilgiler var .
martineau

Yanıtlar:


571

Evet bunu beğendim:

defaultdict(lambda: defaultdict(int))

Varolmayan bir anahtara erişmeye çalıştığınızda defaultdict(bu durumda lambda: defaultdict(int)) argümanı çağrılır. Geri dönüş değeri bu anahtarın yeni değeri olarak ayarlanacaktır, bu da bizim durumumuzda değerinin d[Key_doesnt_exist]olacağı anlamına gelir defaultdict(int).

Bu son varsayılan komuttan bir anahtara erişmeye çalışırsanız, diğer bir deyişle d[Key_doesnt_exist][Key_doesnt_exist], son varsayılan komutun bağımsız değişkeninin dönüş değeri olan 0 değerini döndürür int().


7
harika çalışıyor! bu sözdiziminin arkasındaki mantığı açıklayabilir misiniz?
Jonathan

37
@Jonathan: Evet, var olmayan bir anahtara erişmeye çalıştığınızda defaultdict(bu durumda lambda : defaultdict(int)) a argümanı çağrılır ve anahtarın dönüş değeri, bu anahtarın yeni değeri olarak ayarlanır. bizim durumumuzda değeri d[Key_dont_exist]olacak defaultdict(int)ve siz yani bu son defaultdict bir anahtar erişmeye çalışırsanız d[Key_dont_exist][Key_dont_exist]son argümanı dönüş değeri olan 0 dönecektir defaultdictyani int(), Umut bu yardımcı oldu.
mouad

25
Argümanı defaultdictbir işlev olmalıdır. defaultdict(int)sözlük, sözlük lambda: defaultdict(int)döndüren işlevdir.
has2k1

27
@ has2k1 Bu yanlış. Defaultdict bağımsız değişkeninin çağrılabilir olması gerekir. Bir lambda çağrılabilir.
Niels Bom

2
@RickyLevi, eğer bu çalışmayı yapmak istiyorsanız şunu söyleyebilirsiniz: defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
darophi

51

Defaultdict yapıcısının parametresi, yeni öğeler oluşturmak için çağrılacak işlevdir. Öyleyse bir lambda kullanalım!

>>> from collections import defaultdict
>>> d = defaultdict(lambda : defaultdict(int))
>>> print d[0]
defaultdict(<type 'int'>, {})
>>> print d[0]["x"]
0

Python 2.7'den beri Counter'ı kullanarak daha da iyi bir çözüm var :

>>> from collections import Counter
>>> c = Counter()
>>> c["goodbye"]+=1
>>> c["and thank you"]=42
>>> c["for the fish"]-=5
>>> c
Counter({'and thank you': 42, 'goodbye': 1, 'for the fish': -5})

Bazı bonus özellikler

>>> c.most_common()[:2]
[('and thank you', 42), ('goodbye', 1)]

Daha fazla bilgi için bkz. PyMOTW - Koleksiyonlar - Kapsayıcı veri türleri ve Python Belgeleri - koleksiyonlar


5
Buradaki daireyi tamamlamak için , sorunu orijinal olarak ortaya koyduğu gibi özel olarak ele almak d = defaultdict(lambda : Counter())yerine kullanmak istersiniz d = defaultdict(lambda : defaultdict(int)).
gumption

3
@gumption d = defaultdict(Counter())bu durumda lambdaya gerek yok
Deb

3
@Deb'de küçük bir hata var- iç parantezleri kaldırın, böylece Counternesne yerine çağrılabilir bir öğe iletin . Yani:d = defaultdict(Counter)
Dillon Davis

29

Kullanımı biraz daha zarif buluyorum partial:

import functools
dd_int = functools.partial(defaultdict, int)
defaultdict(dd_int)

Tabii ki, bu bir lambda ile aynı.


1
Kısmi da burada lambda daha iyidir çünkü özyinelemeli olarak uygulanabilir :) Genel iç içe geçmiş defaultdict fabrika yöntemi için aşağıdaki cevabım bakın.
Campi

@Campi, özyinelemeli uygulamalar için kısmi gerekmez, AFAICT
Clément

10

Referans olarak, genel bir iç içe defaultdictfabrika yöntemini şu yöntemlerle uygulamak mümkündür :

from collections import defaultdict
from functools import partial
from itertools import repeat


def nested_defaultdict(default_factory, depth=1):
    result = partial(defaultdict, default_factory)
    for _ in repeat(None, depth - 1):
        result = partial(defaultdict, result)
    return result()

Derinlik, içinde tanımlanan tür default_factorykullanılmadan önce iç içe sözlük sayısını tanımlar . Örneğin:

my_dict = nested_defaultdict(list, 3)
my_dict['a']['b']['c'].append('e')

Bir kullanım örneği verebilir misiniz? Bunu beklediğim gibi çalışmıyor. ndd = nested_defaultdict(dict) .... ndd['a']['b']['c']['d'] = 'e'atarKeyError: 'b'
David Marx

Hey David, sözlüğünüzün derinliğini tanımlamanız gerekiyor, örnek 3'te (default_factory'yi sözlük olarak da tanımladığınız gibi. Nested_defaultdict (dict, 3) sizin için çalışacaktır.
Campi

Bu süper yardımcı oldu, teşekkürler! Fark ettiğim bir şey, bu, çağrılırken depth=0derinlik bilinmiyorsa, her zaman istenmeyebilecek bir default_dict oluşturur . if not depth: return default_factory()Muhtemelen daha zarif bir çözüm olmasına rağmen, işlevin üstüne bir çizgi ekleyerek kolayca sabitlenebilir .
Brendan

9

Önceki cevaplar iki seviyeli veya n seviyeli nasıl yapılacağına değindi defaultdict. Bazı durumlarda sonsuz olanı istersiniz:

def ddict():
    return defaultdict(ddict)

Kullanımı:

>>> d = ddict()
>>> d[1]['a'][True] = 0.5
>>> d[1]['b'] = 3
>>> import pprint; pprint.pprint(d)
defaultdict(<function ddict at 0x7fcac68bf048>,
            {1: defaultdict(<function ddict at 0x7fcac68bf048>,
                            {'a': defaultdict(<function ddict at 0x7fcac68bf048>,
                                              {True: 0.5}),
                             'b': 3})})

1
Bunu seviyorum. Şeytani derecede basit, ama inanılmaz derecede kullanışlı. Teşekkürler!
rosstex

6

Diğerleri aşağıdakileri nasıl çalıştıracağınıza ilişkin sorunuzu doğru bir şekilde yanıtladı:

for x in stuff:
    d[x.a][x.b] += x.c_int

Alternatif olarak anahtarlar için tuples kullanmak olabilir:

d = defaultdict(int)
for x in stuff:
    d[x.a,x.b] += x.c_int
    # ^^^^^^^ tuple key

Bu yaklaşımla ilgili güzel olan şey, basit olması ve kolayca genişletilebilmesidir. Üç seviye derinlikte bir haritalamaya ihtiyacınız varsa, sadece anahtar için üç öğelik bir demet kullanın.


4
Bu çözüm, d [xa] 'nın tümünü elde etmenin basit olmadığı anlamına gelir, çünkü demetin ilk öğesi olarak xa olup olmadığını görmek için her anahtarı içgözlemlemeniz gerekir.
Matthew Schinckel

5
3 seviyeyi derinlere yerleştirmek istiyorsanız, sadece 3 seviye olarak tanımlayın: d = defaultdict (lambda: defaultdict (lambda: defaultdict (int)))
Matthew Schinckel
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.