Wie funktioniert collection.defaultdict?


532

Ich habe die Beispiele in Python-Dokumenten gelesen, kann aber immer noch nicht herausfinden, was diese Methode bedeutet. Kann jemand helfen? Hier sind zwei Beispiele aus den Python-Dokumenten

>>> 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)]

und

>>> 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])]

die Parameter intund listsind für was?


15
Übrigens: Vergessen Sie je nach Anwendungsfall nicht, das Standarddiktat für die schreibgeschützte Verwendung einzufrieren, indem default_factory = NoneSie es festlegen, nachdem Sie das Standarddiktat vollständig ausgefüllt haben. Siehe diese Frage .
Acumenus

Antworten:


598

Normalerweise löst ein Python-Wörterbuch ein aus, KeyErrorwenn Sie versuchen, ein Element mit einem Schlüssel abzurufen, der sich derzeit nicht im Wörterbuch befindet. Im defaultdictGegensatz dazu werden einfach alle Elemente erstellt, auf die Sie zugreifen möchten (vorausgesetzt, sie sind natürlich noch nicht vorhanden). Um ein solches "Standard" -Element zu erstellen, ruft es das Funktionsobjekt auf, das Sie an den Konstruktor übergeben (genauer gesagt, es ist ein beliebiges "aufrufbares" Objekt, das Funktions- und Typobjekte enthält). Im ersten Beispiel werden Standardelemente mit erstellt int(), die das ganzzahlige Objekt zurückgeben 0. Im zweiten Beispiel werden Standardelemente mit erstellt list(), die ein neues leeres Listenobjekt zurückgeben.


4
Unterscheidet es sich funktional von der Verwendung von d.get (key, default_val)?
Ambareesh

29
@Ambareesh d.get(key, default)wird Ihr Wörterbuch niemals ändern - es wird nur die Standardeinstellung zurückgeben und das Wörterbuch unverändert lassen. defaultdictAuf der anderen Seite wird einfügen einen Schlüssel in das Wörterbuch , wenn es noch nicht da ist. Das ist ein großer Unterschied; Sehen Sie sich die Beispiele in der Frage an, um zu verstehen, warum.
Sven Marnach

Woher wissen wir, was der Standardwert für jeden Typ ist? 0 für int () und [] für list () sind intuitiv, es kann aber auch komplexere oder selbst definierte Typen geben.
Sean

1
@Sean defaultdictruft jeden Konstruktor auf, den Sie übergeben. Wenn Sie einen Typ übergeben T, werden Werte mit erstellt T(). Nicht alle Typen können ohne Übergabe von Parametern erstellt werden. Wenn Sie einen solchen Typ erstellen möchten, benötigen Sie eine Wrapper-Funktion oder ähnliches functools.partial(T, arg1, arg2).
Sven Marnach

224

defaultdictbedeutet, dass, wenn ein Schlüssel nicht im Wörterbuch gefunden wird, anstelle eines KeyErrorgeworfenen Schlüssels ein neuer Eintrag erstellt wird. Der Typ dieses neuen Eintrags wird durch das Argument defaultdict angegeben.

Zum Beispiel:

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

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

10
"Der Typ dieses neuen Paares wird durch das Argument von defaultdict angegeben." Beachten Sie, dass das Argument ein beliebiges aufrufbares Objekt sein kann - nicht nur Typfunktionen. Wenn foo beispielsweise eine Funktion ist, die "bar" zurückgibt, kann foo als Argument für das Standarddiktat verwendet werden, und wenn auf einen nicht vorhandenen Schlüssel zugegriffen wird, wird sein Wert auf "bar" gesetzt.
lf215

13
Oder wenn Sie nur "bar" zurückgeben möchten: somedict = defaultdict (lambda: "bar")
Michael Scott Cuthbert

Die vierte Zeile gab 0die Ganzzahl zurück, falls someddict = defaultdict(list)dies der Fall war [ ]. Ist 0 die Standard-Ganzzahl? Oder [] die Standardliste?
Gathide

Weder. 0ist unveränderlich - in CPython sind alle Werte von -5bis 256zwischengespeicherte Singletons, dies ist jedoch ein implementierungsspezifisches Verhalten - in beiden Fällen wird jedes Mal mit int()oder eine neue Instanz "erstellt" list(). Auf diese Weise d[k].append(v)kann gearbeitet werden, ohne das Wörterbuch mit Verweisen auf dieselbe Liste zu füllen, was defaultdictfast unbrauchbar machen würde . Wenn dies das Verhalten defaultdictwäre , würde ein Wert, kein Lambda, als Parameter verwendet. (Entschuldigung für die schreckliche Erklärung!)
wizzwizz4

93

defaultdict

"Das Standardwörterbuch enthält die Methode setdefault () zum Abrufen eines Werts und zum Festlegen eines Standardwerts, wenn der Wert nicht vorhanden ist. Im Gegensatz dazu kann defaultdictder Aufrufer den Standardwert (den zurückzugebenden Wert) vorab angeben, wenn der Container initialisiert wird."

wie von Doug Hellmann in The Python Standard Library by Example definiert

Verwendung von defaultdict

Standarddict importieren

>>> from collections import defaultdict

Standarddict initialisieren

Initialisieren Sie es durch Übergeben

als erstes Argument aufrufbar (obligatorisch)

>>> 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 als zweites Argument (optional)

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

oder

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

Wie funktioniert es

Wie eine untergeordnete Klasse des Standardwörterbuchs kann sie dieselben Funktionen ausführen.

Bei Übergabe eines unbekannten Schlüssels wird jedoch anstelle eines Fehlers der Standardwert zurückgegeben. Zum Beispiel:

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

Wenn Sie den Standardwert ändern möchten, überschreiben Sie 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})

oder

>>> 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})

Beispiele in der Frage

Beispiel 1

Da int als default_factory übergeben wurde, gibt jeder unbekannte Schlüssel standardmäßig 0 zurück.

Wenn der String nun in der Schleife übergeben wird, erhöht sich die Anzahl dieser Alphabete in 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})

Beispiel 2

Da eine Liste als default_factory übergeben wurde, gibt jeder unbekannte (nicht vorhandene) Schlüssel standardmäßig [] (dh Liste) zurück.

Wenn nun die Liste der Tupel in der Schleife übergeben wird, wird der Wert in d [Farbe] angehängt.

>>> 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

Wörterbücher sind eine bequeme Möglichkeit, Daten für den späteren Abruf nach Namen (Schlüssel) zu speichern. Schlüssel müssen eindeutige, unveränderliche Objekte sein und sind normalerweise Zeichenfolgen. Die Werte in einem Wörterbuch können beliebig sein. Für viele Anwendungen sind die Werte einfache Typen wie Ganzzahlen und Zeichenfolgen.

Interessanter wird es, wenn die Werte in einem Wörterbuch Sammlungen (Listen, Diktate usw.) sind. In diesem Fall muss der Wert (eine leere Liste oder ein Diktat) bei der ersten Verwendung eines bestimmten Schlüssels initialisiert werden. Während dies manuell relativ einfach ist, automatisiert und vereinfacht der Standarddict-Typ diese Art von Vorgängen. Ein Standarddikt funktioniert genau wie ein normales Diktat, wird jedoch mit einer Funktion („Standardfactory“) initialisiert, die keine Argumente akzeptiert und den Standardwert für einen nicht vorhandenen Schlüssel bereitstellt.

Ein Standarddikt löst niemals einen KeyError aus. Jeder nicht vorhandene Schlüssel erhält den von der Standardfactory zurückgegebenen Wert.

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

Hier ist ein weiteres Beispiel, wie wir mit defaultdict die Komplexität reduzieren können

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))

Wenn Sie ein Wörterbuch benötigen und der Wert jedes Elements mit einem Standardwert beginnen sollte, verwenden Sie abschließend ein Standarddiktat.


18

Hier finden Sie eine gute Erklärung für Standardverbote: http://ludovf.net/blog/python-collections-defaultdict/

Grundsätzlich sind die Parameter int und list Funktionen, die Sie übergeben. Denken Sie daran, dass Python Funktionsnamen als Argumente akzeptiert. int gibt standardmäßig 0 zurück und list gibt eine leere Liste zurück, wenn sie mit Klammern aufgerufen wird.

Wenn ich in normalen Wörterbüchern in Ihrem Beispiel versuche aufzurufen d[a], wird eine Fehlermeldung (KeyError) angezeigt, da nur die Schlüssel m, s, i und p vorhanden sind und der Schlüssel a nicht initialisiert wurde. In einem Standarddikt wird jedoch ein Funktionsname als Argument verwendet. Wenn Sie versuchen, einen Schlüssel zu verwenden, der nicht initialisiert wurde, ruft er einfach die übergebene Funktion auf und weist ihren Rückgabewert als Wert des neuen Schlüssels zu.


7

Da es um "wie es funktioniert" geht, möchten einige Leser vielleicht mehr Schrauben und Muttern sehen. Insbesondere ist die fragliche __missing__(key)Methode die Methode. Siehe: https://docs.python.org/2/library/collections.html#defaultdict-objects .

Genauer gesagt zeigt diese Antwort, wie man sie auf __missing__(key)praktische Weise nutzt : https://stackoverflow.com/a/17956989/1593924

Um zu verdeutlichen, was "aufrufbar" bedeutet, folgt eine interaktive Sitzung (ab 2.7.6, sollte aber auch in Version 3 funktionieren):

>>> 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})

Dies war die typischste Verwendung von defaultdict (mit Ausnahme der sinnlosen Verwendung der Variablen x). Sie können dasselbe mit 0 als expliziten Standardwert tun, jedoch nicht mit einem einfachen Wert:

>>> dd2 = defaultdict(0)

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

Stattdessen funktioniert Folgendes, weil es eine einfache Funktion übergibt (es erstellt im laufenden Betrieb eine namenlose Funktion, die keine Argumente akzeptiert und immer 0 zurückgibt):

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

Und mit einem anderen Standardwert:

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

7

Meine eigene 2 ¢: Sie können auch defaultdict unterordnen:

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

Dies kann in sehr komplexen Fällen nützlich sein.


4

Das Verhalten von defaultdictkann leicht nachgeahmt werden, dict.setdefaultanstatt d[key]bei jedem Anruf.

Mit anderen Worten, der Code:

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]

ist äquivalent zu:

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]

Der einzige Unterschied besteht darin, dass der Listenkonstruktor bei defaultdictVerwendung nur einmal aufgerufen wird und die Verwendung dict.setdefaultdes Listenkonstruktors häufiger aufgerufen wird (der Code kann jedoch möglicherweise neu geschrieben werden, um dies zu vermeiden, falls dies wirklich erforderlich ist).

Einige mögen argumentieren, dass es eine Leistungsüberlegung gibt, aber dieses Thema ist ein Minenfeld. Dieser Beitrag zeigt, dass die Verwendung von defaultdict zum Beispiel keinen großen Leistungsgewinn bringt.

IMO, defaultdict ist eine Sammlung, die dem Code mehr Verwirrung als Vorteile bringt. Für mich nutzlos, aber andere denken vielleicht anders.


3

Das defaultdict-Tool ist ein Container in der Sammlungsklasse von Python. Es ähnelt dem üblichen Wörterbuchcontainer (dict), hat jedoch einen Unterschied: Der Datentyp der Wertefelder wird bei der Initialisierung angegeben.

Zum Beispiel:

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

Dies druckt:

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

"Der Datentyp der Wertefelder wird bei der Initialisierung angegeben": Dies ist nicht korrekt. Eine Elementfabrikfunktion wird bereitgestellt. Hier listist die Funktion, die aufgerufen werden muss, um einen fehlenden Wert einzugeben, nicht den Typ der zu erstellenden Objekte. Um beispielsweise einen Standardwert von zu haben 1, würden Sie verwenden, lambda:1der offensichtlich kein Typ ist.
Asac

2

Ich denke, es wird am besten anstelle einer switch case-Anweisung verwendet. Stellen Sie sich vor, wir haben eine Switch-Case-Anweisung wie folgt:

option = 1

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

switchIn Python sind keine case-Anweisungen verfügbar. Wir können das gleiche erreichen, indem wir verwenden 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])

Es druckt:

Default Value
Default Value
3rd option

Im obigen Snippet ddgibt es keine Tasten 4 oder 5 und daher wird ein Standardwert ausgedruckt, den wir in einer Hilfsfunktion konfiguriert haben. Dies ist viel schöner als ein Rohwörterbuch, bei dem a KeyErrorausgelöst wird, wenn der Schlüssel nicht vorhanden ist. Daraus ergibt sich, dass es sich defaultdicteher um eine Switch-Case-Anweisung handelt, bei der komplizierte if-elif-elif-elseBlöcke vermieden werden können.

Ein weiteres gutes Beispiel, das mich auf dieser Website sehr beeindruckt hat, ist:

>>> 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})
>>>

Wenn wir versuchen , alle Elemente für den Zugriff auf andere als eggsund spamwir werden eine Anzahl von 0 erhalten.


2

Ohne defaultdictkönnen Sie möglicherweise unsichtbaren Schlüsseln neue Werte zuweisen, diese jedoch nicht ändern. Zum Beispiel:

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

Nun, defaultdict kann im folgenden Fall auch den Schlüsselfehler erhöhen:

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

Denken Sie immer daran, dem defaultdict wie defaultdict (int) Argumente zu geben.


0

Das Standardwörterbuch enthält die Methode setdefault () zum Abrufen eines Werts und zum Festlegen eines Standards, wenn der Wert nicht vorhanden ist. Im Gegensatz dazu kann der Anrufer mit defaultdict die Standardeinstellung im Voraus festlegen, wenn der Container initialisiert wird.

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']

Dies funktioniert gut, solange alle Schlüssel dieselbe Standardeinstellung haben. Dies kann besonders nützlich sein, wenn der Standard ein Typ ist, der zum Aggregieren oder Akkumulieren von Werten verwendet wird, z. B. eine Liste, eine Menge oder sogar int. Die Standardbibliotheksdokumentation enthält mehrere Beispiele für die Verwendung von defaultdict auf diese Weise.

$ python collections_defaultdict.py

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

0

Zusamenfassend:

defaultdict(int) - Das Argument int gibt an, dass die Werte vom Typ int sind.

defaultdict(list) - Die Argumentliste gibt an, dass die Werte vom Listentyp sind.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.