Jak działa collections.defaultdict?


531

Przeczytałem przykłady w dokumentach Pythona, ale wciąż nie mogę zrozumieć, co oznacza ta metoda. Czy ktoś może pomóc? Oto dwa przykłady z dokumentacji Pythona

>>> from collections import defaultdict

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
...     d[k] += 1
...
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]

i

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
...     d[k].append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

parametry inti listpo co?


15
BTW, w zależności od przypadku użycia, nie zapomnij zamrozić domyślnego dykta do użytku tylko do odczytu, ustawiając go default_factory = Nonepo zakończeniu wypełniania domyślnego dykta. Zobacz to pytanie .
Acumenus,

Odpowiedzi:


598

Zwykle słownik Pythona rzuca, KeyErrorjeśli próbujesz uzyskać element z kluczem, którego nie ma obecnie w słowniku. W defaultdictprzeciwieństwie do tego po prostu utworzy wszystkie przedmioty, do których próbujesz uzyskać dostęp (oczywiście pod warunkiem, że jeszcze nie istnieją). Aby utworzyć taki „domyślny” element, wywołuje on obiekt funkcji, który przekazujesz do konstruktora (a ściślej, jest to dowolny obiekt „na żądanie”, który obejmuje obiekty funkcji i typu). W pierwszym przykładzie elementy domyślne są tworzone przy użyciu int(), który zwraca obiekt liczby całkowitej 0. W drugim przykładzie elementy domyślne są tworzone przy użyciu list(), który zwraca nowy pusty obiekt listy.


4
Czy funkcjonalnie różni się od używania d.get (key, default_val)?
Ambareesh

29
@Ambareesh d.get(key, default)nigdy nie zmodyfikuje Twojego słownika - po prostu zwróci domyślny i pozostawi słownik bez zmian. defaultdictz drugiej strony wstawi klucz do słownika, jeśli jeszcze go nie ma. To duża różnica; zobacz przykłady w pytaniu, aby zrozumieć, dlaczego.
Sven Marnach

Skąd wiemy, jaka jest wartość domyślna dla każdego typu? 0 dla int () i [] dla list () są intuicyjne, ale mogą być też bardziej złożone lub samodzielnie zdefiniowane typy.
Sean

1
@Sean defaultdictwywołuje dowolny przekazany konstruktor. Jeśli podasz typ T, wartości zostaną zbudowane przy użyciu T(). Nie wszystkie typy można konstruować bez przekazywania jakichkolwiek parametrów. Jeśli chcesz zbudować taki typ, potrzebujesz funkcji otoki lub czegoś podobnego functools.partial(T, arg1, arg2).
Sven Marnach

224

defaultdictoznacza, że ​​jeśli klucz nie zostanie znaleziony w słowniku, zamiast KeyErrorzostać wyrzuconym, tworzony jest nowy wpis. Typ tego nowego wpisu podaje argument defaultdict.

Na przykład:

somedict = {}
print(somedict[3]) # KeyError

someddict = defaultdict(int)
print(someddict[3]) # print int(), thus 0

10
„Typ tej nowej pary określa argument defaultdict”. Zauważ, że argumentem może być dowolny obiekt wywoływalny - nie tylko funkcje typu. Na przykład, jeśli foo był funkcją, która zwróciła „bar”, foo może być użyte jako argument do domyślnego dyktowania, a jeśli uzyskano dostęp do nieobecnego klucza, jego wartość byłaby ustawiona na „bar”.
lf215

13
Lub jeśli chcesz tylko zwrócić „bar”: somedict = defaultdict (lambda: „bar”)
Michael Scott Cuthbert

Czwarta linia zwróciła 0liczbę całkowitą, jeśli tak, someddict = defaultdict(list)to zwraca [ ]. Czy 0 jest domyślną liczbą całkowitą? Lub [] domyślna lista?
Gathide,

Ani. 0jest niezmienny - w CPython wszystkie wartości od -5do 256są buforowanymi singletonami, ale jest to zachowanie specyficzne dla implementacji - w obu przypadkach nowa instancja jest „tworzona” za każdym razem za pomocą int()lub list(). W ten sposób d[k].append(v)może działać bez wypełniania słownika odniesieniami do tej samej listy, co uczyniłoby go defaultdictprawie bezużytecznym. Gdyby takie było zachowanie, defaultdictwziąłby jako parametr wartość, a nie lambda. (Przepraszam za okropne wyjaśnienie!)
wizzwizz4,

93

defaultdict

„Słownik standardowy zawiera metodę setdefault () służącą do pobierania wartości i ustanawiania wartości domyślnej, jeśli wartość nie istnieje. Natomiast defaultdictprogram wywołujący może określić wartość domyślną (wartość do zwrócenia) z góry podczas inicjowania kontenera.”

jak zdefiniował Doug Hellmann w The Standard Python Library przez przykład

Jak korzystać z defaultdict

Importuj defaultdict

>>> from collections import defaultdict

Zainicjuj defaultdict

Zainicjuj go, przechodząc

jako pierwszy argument na żądanie (obowiązkowe)

>>> d_int = defaultdict(int)
>>> d_list = defaultdict(list)
>>> def foo():
...     return 'default value'
... 
>>> d_foo = defaultdict(foo)
>>> d_int
defaultdict(<type 'int'>, {})
>>> d_list
defaultdict(<type 'list'>, {})
>>> d_foo
defaultdict(<function foo at 0x7f34a0a69578>, {})

** kwargs jako drugi argument (opcjonalnie)

>>> d_int = defaultdict(int, a=10, b=12, c=13)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})

lub

>>> kwargs = {'a':10,'b':12,'c':13}
>>> d_int = defaultdict(int, **kwargs)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})

Jak to działa

Ponieważ jest klasą potomną standardowego słownika, może wykonywać te same funkcje.

Ale w przypadku przekazania nieznanego klucza zwraca wartość domyślną zamiast błędu. Na przykład:

>>> d_int['a']
10
>>> d_int['d']
0
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12, 'd': 0})

W przypadku, gdy chcesz zmienić wartość domyślną, nadpisz default_factory:

>>> d_int.default_factory = lambda: 1
>>> d_int['e']
1
>>> d_int
defaultdict(<function <lambda> at 0x7f34a0a91578>, {'a': 10, 'c': 13, 'b': 12, 'e': 1, 'd': 0})

lub

>>> def foo():
...     return 2
>>> d_int.default_factory = foo
>>> d_int['f']
2
>>> d_int
defaultdict(<function foo at 0x7f34a0a0a140>, {'a': 10, 'c': 13, 'b': 12, 'e': 1, 'd': 0, 'f': 2})

Przykłady w pytaniu

Przykład 1

Ponieważ int został przekazany jako default_factory, każdy nieznany klucz domyślnie zwróci 0.

Teraz, gdy ciąg zostanie przekazany w pętli, zwiększy liczbę tych alfabetów w d.

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> d.default_factory
<type 'int'>
>>> for k in s:
...     d[k] += 1
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]
>>> d
defaultdict(<type 'int'>, {'i': 4, 'p': 2, 's': 4, 'm': 1})

Przykład 2

Ponieważ lista została przekazana jako default_factory, każdy nieznany (nieistniejący) klucz domyślnie zwróci [] (tj. Listę).

Teraz, gdy lista krotek jest przekazywana w pętli, dołączy wartość w d [kolor]

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> d.default_factory
<type 'list'>
>>> for k, v in s:
...     d[k].append(v)
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
>>> d
defaultdict(<type 'list'>, {'blue': [2, 4], 'red': [1], 'yellow': [1, 3]})

20

Słowniki to wygodny sposób przechowywania danych do późniejszego pobrania według nazwy (klucza). Klucze muszą być unikalnymi, niezmiennymi obiektami i zazwyczaj są łańcuchami. Wartości w słowniku mogą być dowolne. W wielu aplikacjach wartościami są proste typy, takie jak liczby całkowite i łańcuchy.

Staje się bardziej interesujący, gdy wartościami w słowniku są kolekcje (listy, dykta itp.). W takim przypadku wartość (pusta lista lub dykta) musi zostać zainicjowana przy pierwszym użyciu danego klucza. Chociaż jest to stosunkowo łatwe do zrobienia ręcznie, typ defaultdict automatyzuje i upraszcza tego rodzaju operacje. Domyślny dykta działa dokładnie tak jak normalny dykt, ale jest inicjowany funkcją („domyślną fabryką”), która nie przyjmuje żadnych argumentów i podaje wartość domyślną dla nieistniejącego klucza.

Defaultdict nigdy nie zgłosi KeyError. Każdy nieistniejący klucz otrzymuje wartość zwracaną przez domyślną fabrykę.

from collections import defaultdict
ice_cream = defaultdict(lambda: 'Vanilla')

ice_cream['Sarah'] = 'Chunky Monkey'
ice_cream['Abdul'] = 'Butter Pecan'

print(ice_cream['Sarah'])
>>>Chunky Monkey

print(ice_cream['Joe'])
>>>Vanilla

Oto kolejny przykład dotyczący tego, jak za pomocą defaultdict możemy zmniejszyć złożoność

from collections import defaultdict
# Time complexity O(n^2)
def delete_nth_naive(array, n):
    ans = []
    for num in array:
        if ans.count(num) < n:
            ans.append(num)
    return ans

# Time Complexity O(n), using hash tables.
def delete_nth(array,n):
    result = []
    counts = defaultdict(int)

    for i in array:
        if counts[i] < n:
            result.append(i)
            counts[i] += 1
    return result


x = [1,2,3,1,2,1,2,3]
print(delete_nth(x, n=2))
print(delete_nth_naive(x, n=2))

Podsumowując, ilekroć potrzebujesz słownika, a wartość każdego elementu powinna zaczynać się od wartości domyślnej, użyj defaultdict.


18

Świetne wyjaśnienie defaultdicts tutaj: http://ludovf.net/blog/python-collections-defaultdict/

Zasadniczo parametry int i lista są funkcjami, które przekazujesz. Pamiętaj, że Python akceptuje nazwy funkcji jako argumenty. int zwraca 0 domyślnie i wyświetla listę zwraca pustą listę, gdy jest wywoływana w nawiasach.

W normalnych słownikach, jeśli w twoim przykładzie spróbuję zadzwonić d[a], otrzymam błąd (KeyError), ponieważ istnieją tylko klucze m, s, i ip, a klucz a nie został zainicjowany. Ale w defaultdict bierze nazwę funkcji jako argument, kiedy próbujesz użyć klucza, który nie został zainicjowany, po prostu wywołuje przekazaną funkcję i przypisuje jej wartość zwracaną jako wartość nowego klucza.


7

Ponieważ pytanie dotyczy „jak to działa”, niektórzy czytelnicy mogą chcieć zobaczyć więcej nakrętek i śrub. W szczególności omawiana metoda jest __missing__(key)metodą. Zobacz: https://docs.python.org/2/library/collections.html#defaultdict-objects .

Mówiąc bardziej konkretnie, ta odpowiedź pokazuje, jak wykorzystać __missing__(key)w praktyczny sposób: https://stackoverflow.com/a/17956989/1593924

Aby wyjaśnić, co oznacza „wywoływalny”, oto interaktywna sesja (od 2.7.6, ale powinna również działać w wersji 3):

>>> x = int
>>> x
<type 'int'>
>>> y = int(5)
>>> y
5
>>> z = x(5)
>>> z
5

>>> from collections import defaultdict
>>> dd = defaultdict(int)
>>> dd
defaultdict(<type 'int'>, {})
>>> dd = defaultdict(x)
>>> dd
defaultdict(<type 'int'>, {})
>>> dd['a']
0
>>> dd
defaultdict(<type 'int'>, {'a': 0})

Było to najbardziej typowe użycie defaultdict (z wyjątkiem bezcelowego użycia zmiennej x). Możesz zrobić to samo z 0 jako jawną wartością domyślną, ale nie z prostą wartością:

>>> dd2 = defaultdict(0)

Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    dd2 = defaultdict(0)
TypeError: first argument must be callable

Zamiast tego działa następująco, ponieważ przekazuje w prostej funkcji (tworzy w locie funkcję bezimienną, która nie przyjmuje argumentów i zawsze zwraca 0):

>>> dd2 = defaultdict(lambda: 0)
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {})
>>> dd2['a']
0
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {'a': 0})
>>> 

I z inną wartością domyślną:

>>> dd3 = defaultdict(lambda: 1)
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {})
>>> dd3['a']
1
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {'a': 1})
>>> 

7

Mój własny 2 ¢: możesz również podklasę defaultdict:

class MyDict(defaultdict):
    def __missing__(self, key):
        value = [None, None]
        self[key] = value
        return value

Może się to przydać w bardzo skomplikowanych przypadkach.


4

Zachowanie defaultdictmożna łatwo naśladować za pomocą dict.setdefaultzamiastd[key] w każdym połączeniu.

Innymi słowy, kod:

from collections import defaultdict

d = defaultdict(list)

print(d['key'])                        # empty list []
d['key'].append(1)                     # adding constant 1 to the list
print(d['key'])                        # list containing the constant [1]

jest równa:

d = dict()

print(d.setdefault('key', list()))     # empty list []
d.setdefault('key', list()).append(1)  # adding constant 1 to the list
print(d.setdefault('key', list()))     # list containing the constant [1]

Jedyna różnica polega na tym, że przy użyciu defaultdictkonstruktor listy jest wywoływany tylko raz, a przy użyciu dict.setdefaultkonstruktora listy jest wywoływany częściej (ale kod może zostać przepisany, aby tego uniknąć, jeśli jest to naprawdę potrzebne).

Niektórzy mogą twierdzić, że rozważa się wydajność, ale ten temat to pole minowe. Ten post pokazuje, że nie ma dużego wzrostu wydajności, na przykład przy użyciu defaultdict.

IMO, defaultdict to kolekcja, która wprowadza więcej zamieszania niż korzyści w kodzie. Dla mnie bezużyteczne, ale inni mogą myśleć inaczej.


3

Narzędzie defaultdict jest kontenerem w klasie kolekcji Pythona. Jest podobny do zwykłego kontenera słownika (dict), ale ma jedną różnicę: typ danych pól wartości jest określany podczas inicjalizacji.

Na przykład:

from collections import defaultdict

d = defaultdict(list)

d['python'].append("awesome")

d['something-else'].append("not relevant")

d['python'].append("language")

for i in d.items():

    print i

To drukuje:

('python', ['awesome', 'language'])
('something-else', ['not relevant'])

„Typ danych pól wartości jest określony podczas inicjalizacji”: nie jest to poprawne. Zapewniona jest funkcja fabryki elementów. Oto listfunkcja do wywołania w celu uzupełnienia brakującej wartości, a nie typu obiektów do utworzenia. Na przykład, aby mieć wartość domyślną 1, należy użyć, lambda:1który oczywiście nie jest typem.
asac

2

Myślę, że najlepiej stosować go zamiast instrukcji skrzynki rozdzielczej. Wyobraź sobie, że mamy instrukcję zmiany przypadku, jak poniżej:

option = 1

switch(option) {
    case 1: print '1st option'
    case 2: print '2nd option'
    case 3: print '3rd option'
    default: return 'No such option'
}

W switchpython nie ma dostępnych instrukcji case. Możemy osiągnąć to samo, używając defaultdict.

from collections import defaultdict

def default_value(): return "Default Value"
dd = defaultdict(default_value)

dd[1] = '1st option'
dd[2] = '2nd option'
dd[3] = '3rd option'

print(dd[4])    
print(dd[5])    
print(dd[3])

Drukuje:

Default Value
Default Value
3rd option

W powyższym fragmencie ddnie ma klawiszy 4 ani 5, a zatem wypisuje domyślną wartość, którą skonfigurowaliśmy w funkcji pomocnika. Jest to o wiele ładniejsze niż nieprzetworzony słownik, w którym KeyErrorwyrzuca się, jeśli klucz nie jest obecny. Z tego wynika, że defaultdictbardziej przypomina instrukcję przypadku przełącznika, w której możemy uniknąć skomplikowanych if-elif-elif-elsebloków.

Jeszcze jeden dobry przykład, który wywarł na mnie duże wrażenie z tej strony :

>>> from collections import defaultdict
>>> food_list = 'spam spam spam spam spam spam eggs spam'.split()
>>> food_count = defaultdict(int) # default value of int is 0
>>> for food in food_list:
...     food_count[food] += 1 # increment element's value by 1
...
defaultdict(<type 'int'>, {'eggs': 1, 'spam': 7})
>>>

Jeśli spróbujemy uzyskać dostęp do elementów innych niż eggsi spamotrzymamy liczbę 0.


2

Bez defaultdicttego prawdopodobnie możesz przypisać nowe wartości do niewidocznych kluczy, ale nie możesz go zmodyfikować. Na przykład:

import collections
d = collections.defaultdict(int)
for i in range(10):
  d[i] += i
print(d)
# Output: defaultdict(<class 'int'>, {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9})

import collections
d = {}
for i in range(10):
  d[i] += i
print(d)
# Output: Traceback (most recent call last): File "python", line 4, in <module> KeyError: 0

2

Cóż, defaultdict może również podnieść błąd klucza w następującym przypadku:

    from collections import defaultdict
    d = defaultdict()
    print(d[3]) #raises keyerror

Zawsze pamiętaj o podaniu argumentu defaultdict, np. Defaultdict (int).


0

Słownik standardowy zawiera metodę setdefault () do pobierania wartości i ustanawiania wartości domyślnej, jeśli wartość nie istnieje. Natomiast defaultdict pozwala dzwoniącemu określić domyślną wartość z góry podczas inicjowania kontenera.

import collections

def default_factory():
    return 'default value'

d = collections.defaultdict(default_factory, foo='bar')
print 'd:', d
print 'foo =>', d['foo']
print 'bar =>', d['bar']

Działa to dobrze, o ile właściwe jest, aby wszystkie klucze miały takie same wartości domyślne. Może być szczególnie użyteczny, jeśli domyślnym jest typ używany do agregowania lub akumulowania wartości, takich jak lista, zestaw, a nawet liczba całkowita. Standardowa dokumentacja biblioteki zawiera kilka przykładów użycia defaultdict w ten sposób.

$ python collections_defaultdict.py

d: defaultdict(<function default_factory at 0x100468c80>, {'foo': 'bar'})
foo => bar
bar => default value

0

W skrócie:

defaultdict(int) - argument int wskazuje, że wartości będą typu int.

defaultdict(list) - lista argumentów wskazuje, że wartości będą typu listy.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.