2010-07-29 9 views
9

Ich frage mich, wie man ein fehlerverzeihendes Wörterbuch erstellt (eines, das einen Standardwert zurückgibt, wenn ein KeyError ausgelöst wird).Ein fehlerverzeihendes Wörterbuch

Im folgenden Codebeispiel würde ich einen KeyError bekommen; zum Beispiel

a = {'one':1,'two':2} 
print a['three'] 

Um nicht eins zu bekommen, würde ich 1. die Ausnahme fangen oder nutzen.

Ich mag würde nicht zu haben, dass mit meinem Wörterbuch zu tun ...

+2

'collections.defaultdict' ist Ihre Batterien inklusive Lösung. –

+1

+1 für den Fragetitel –

Antwort

22
import collections 
a = collections.defaultdict(lambda: 3) 
a.update({'one':1,'two':2}) 
print a['three'] 

3 emittiert je nach Bedarf. Sie könnten auch dict selbst Unterklasse und überschreiben __missing__, aber das macht nicht viel Sinn machen, wenn das defaultdict Verhalten (die genaue fehlenden Schlüssel zu ignorieren, die nachgeschlagen Sein) zu Ihnen passt so gut ...

bearbeiten ... , es sei denn, Sie sind besorgt um a wächst um einen Eintrag jedes Mal, wenn Sie einen fehlenden Schlüssel (der Teil der Semantik defaultdict ist) suchen und lieber langsamer Verhalten, aber etwas Speicher sparen. Zum Beispiel in Bezug auf Speicher ...:

>>> import sys 
>>> a = collections.defaultdict(lambda: 'blah') 
>>> print len(a), sys.getsizeof(a) 
0 140 
>>> for i in xrange(99): _ = a[i] 
... 
>>> print len(a), sys.getsizeof(a) 
99 6284 

... die defaultdict, ursprünglich leer, jetzt hat die 99 zuvor fehlende Schlüssel, die wir nachgeschlagen, und nimmt 6284 Bytes (im Vergleich zu dem 140 Bytes es dauerte, als es leer war).

Der alternative Ansatz ...:

>>> class mydict(dict): 
... def __missing__(self, key): return 3 
... 
>>> a = mydict() 
>>> print len(a), sys.getsizeof(a) 
0 140 
>>> for i in xrange(99): _ = a[i] 
... 
>>> print len(a), sys.getsizeof(a) 
0 140 

... speichert völlig diesen Speicher-Overhead, wie Sie sehen. Natürlich ist die Leistung ein weiteres Problem:

$ python -mtimeit -s'import collections; a=collections.defaultdict(int); r=xrange(99)' 'for i in r: _=a[i]' 
100000 loops, best of 3: 14.9 usec per loop 

$ python -mtimeit -s'class mydict(dict): 
> def __missing__(self, key): return 0 
> ' -s'a=mydict(); r=xrange(99)' 'for i in r: _=a[i]' 
10000 loops, best of 3: 92.9 usec per loop 

Seit defaultdict fügt die (bisher fehlenden) Taste auf Lookup, es viel schneller wird, wenn ein solcher Schlüssel nächsten nachgeschlagen, während mydict (die __missing__ außer Kraft gesetzt, das zu vermeiden addition) zahlt jedes Mal den "fehlenden Key Lookup Overhead".

Ob Sie sich für ein Problem interessieren (Performance vs. Memory Footprint), hängt natürlich ganz von Ihrem speziellen Anwendungsfall ab. Es ist in jedem Fall eine gute Idee des Kompromisses zu beachten -)

+3

Warnung: defaultdict fügt ein neues Element in sich selbst ein, wenn es den Standardwert für einen bestimmten Schlüssel zurückgibt. Dadurch werden Leseoperationen in mögliche Schreibvorgänge umgewandelt, und das bedeutet, dass das Nachschlagen vieler fehlender Schlüssel zu einem schnellen Wachstum führt. http://docs.python.org/library/collections.html#collections.defaultdict.__missing__ –

+0

@Forest, guter Punkt! Lass mich entsprechend editieren. –

+0

Ausgezeichneter Beitrag! Ihr vorletzter Absatz scheint sich nicht auf Ihr Beispiel zu beziehen, da Sie niemals denselben Schlüssel zweimal verwenden. So scheint es, dass defaultdict schneller ist, selbst wenn Sie nie einen Schlüssel wiederholen und sogar schneller, wenn Sie dies tun. Ist das richtig? –

7

Neu in Version 2.5: Wenn eine Unterklasse von dict einen Verfahren __missing __() definiert, , wenn der Schlüssel Schlüssel ist nicht vorhanden, die d [Schlüssel] -Operation ruft diese Methode mit der Schlüsseltaste als Argument auf. Die d [Schlüssel] -Operation kehrt dann zurück, oder erhöht alles, was zurückgegeben oder ausgelöst wird durch den __missing __ (Schlüssel) Anruf, wenn der Schlüssel nicht vorhanden ist. Keine anderen Operationen oder Methoden aufrufen __missing __(). Wenn __missing __() nicht definiert ist, wird KeyError ausgelöst. __missing __() muss eine Methode sein; Es kann keine Instanzvariable sein. Ein Beispiel für finden Sie unter collections.defaultdict.

http://docs.python.org/library/stdtypes.html

3

Sie werden wahrscheinlich eine defaultdict verwenden wollen (es erfordert atleast python2.5 ich glaube)

from collections import defaultdict 
def default(): return 'Default Value' 
d = defaultdict(default) 
print(d['?']) 

Die Funktion, die an den Konstruktor übergeben wird erzählt der Klasse, was zu Rückgabe als Standardwert Weitere Beispiele finden Sie unter the documentation.

5

Hier ist, wie dict Unterklasse wie von NullUserException vorgeschlagen

>>> class forgiving_dict(dict): 
...  def __missing__(self, key): 
...   return 3 
... 
>>> a = forgiving_dict() 
>>> a.update({'one':1,'two':2}) 
>>> print a['three'] 
3 

Ein großer Unterschied zwischen dieser Antwort und Alex 'ist, dass der fehlende Schlüssel ist nicht dem Wörterbuch hinzugefügt

>>> print a 
{'two': 2, 'one': 1} 

Welche ist ziemlich signifikant, wenn Sie eine Menge Fehler erwarten

0

Manchmal, was Sie re Verbündeter will ist .setdefault(), die nicht sehr intuitiv ist, aber es ist eine Methode, die "den angegebenen Schlüssel zurückgibt, wenn es nicht existiert, setzen Sie diesen Schlüssel auf diesen Wert". Hier

ist ein Beispiel für setdefault() mit guter Wirkung verwendet werden:

collection = {} 
for elem in mylist: 
    key = key_from_elem(elem) 
    collection.setdefault(key, []).append(elem) 

Dies wird es uns ermöglichen, einen Wörterbuch zu erstellen wie: {'key1':[elem1, elem3], 'key2':[elem3]} ohne einen hässlichen Scheck zu haben, um zu sehen, ob es ein Schlüssel ist schon da und Erstellen Sie eine Liste dafür.