2010-09-09 5 views
5

Wie funktioniert:Wie vergleicht sich die Leistung von Dictionary-Schlüssel-Lookups in Python?

dict = {} 
if key not in dict: 
dict[key] = foo 

vergleichen:

try: 
dict[key] 
except KeyError: 
dict[key] = foo 

dh der Look eines Schlüssels in ohnehin schneller als die lineare Suche durch dict.keys(), dass ich die erste Form annehmen tun?

+2

Es gibt auch die dict.setdefault-Methode: http://docs.python.org/release/2.6.6/library/stdtypes.html#mapping-types-dict – GWW

+10

Die erste tut ** nicht ** eine lineare suchen. Wie Larry Wall es ausdrückte: "Lineare Scans über ein assoziatives Array zu machen ist wie der Versuch, jemanden mit einer geladenen Uzi zu Tode zu schlagen." "dict .__ contains__" entspricht in etwa den ersten 2/3 von 'dict.__getitem__' (ein Hash-Lookup). – delnan

+3

Das ist ein tolles Zitat. – nmichaels

Antwort

4

Die Antwort hängt davon ab, wie oft der Schlüssel ist bereits im dict (BTW, hat jemand zu Ihnen erwähnt, wie schlecht eine Idee es ist, eine builtin wie dict hinter einer Variablen zu verbergen?)

if key not in dct: 
dct[key] = foo 

Wenn sich der Schlüssel im Wörterbuch befindet, wird ein Wörterbuch gesucht. Wenn sich der Schlüssel im Wörterbuch befindet, wird das Wörterbuch zweimal nachgeschlagen.

try: 
dct[key] 
except KeyError: 
dct[key] = foo 

für den Fall Dies können etwas schneller sein, wenn der Schlüssel im Wörterbuch enthalten ist, aber eine Ausnahme hat ziemlich großen Kopfwerfer, so ist es fast immer nicht die beste Option.

dct.setdefault(key, foo) 

Dieser ist etwas heikel: Es geht immer zwei Wörterbuch-Lookups: die erste ist die setdefault Methode in der dict Klasse zu finden, ist die zweite für key im dct Objekt zu suchen. Auch wenn foo ein Ausdruck ist, wird er jedes Mal ausgewertet, während die früheren Optionen ihn nur dann auswerten, wenn sie es müssen.

Siehe auch collections.defaultdict. Das ist die beste Lösung für eine große Klasse von Situationen wie dieser.

+1

Guter Punkt bei der Verwendung von 'dict' Ich änderte den Variablennamen beim Eintippen des Beispiels und dachte nicht darüber nach. Der Schlüsselschlüssel ist normalerweise nicht im Diktat. –

+0

Ich werde mit collections.defaultdict gehen, danke, dass Sie darauf hingewiesen haben. Es scheint Python, und ein Haar schneller als dict.setdefault() –

+0

versuchen Profiling bro – coleifer

-1

my_dict.get(key, foo) gibt foo zurück, wenn der Schlüssel nicht in my_dict ist. Der Standardwert ist None. Daher gibt my_dict.get(key) None zurück, wenn der Schlüssel nicht in my_dict ist. Die erste Ihrer Optionen ist besser, wenn Sie nur einen Schlüssel zu Ihrem Wörterbuch hinzufügen möchten. Mach dir keine Sorgen über die Geschwindigkeit hier. Wenn Sie feststellen, dass das Auffüllen Ihres Wörterbuchs ein Hotspot in Ihrem Programm ist, dann denken Sie darüber nach. Aber es ist nicht. Also nicht.

+0

+1 - Sehr Pythonic. – duffymo

+1

Das setzt den Wert nicht, wenn es nicht durch das Betrachten seines Codes eingestellt wird. Es scheint, dass er prüft, ob der Schlüssel existiert und es anders setzt. – GWW

+0

@GWW: True. Sie könnten 'dict [key] = dict.get (key, foo)' aber verwenden. – nmichaels

4

Versuchen Sie: my_dict.setdefault(key, default). Es ist jedoch etwas langsamer als die anderen Optionen.

Wenn key im Wörterbuch ist, geben Sie den Wert zurück. Wenn nicht, geben Sie key mit einem Wert von default ein und geben Sie default zurück. default ist standardmäßig auf Keine eingestellt.

#!/usr/bin/env python 

example_dict = dict(zip(range(10), range(10))) 

def kn(key, d): 
    if key not in d: 
     d[key] = 'foo' 

def te(key, d): 
    try: 
     d[key] 
    except KeyError: 
     d[key] = 'foo' 

def sd(key, d): 
    d.setdefault(key, 'foo') 

if __name__ == '__main__': 
    from timeit import Timer 

    t = Timer("kn(2, example_dict)", "from __main__ import kn, example_dict") 
    print t.timeit() 
    t = Timer("te(2, example_dict)", "from __main__ import te, example_dict") 
    print t.timeit() 
    t = Timer("sd(2, example_dict)", "from __main__ import sd, example_dict") 
    print t.timeit() 

    # kn: 0.249855041504 
    # te: 0.244259119034 
    # sd: 0.375113964081 
+0

Es ist ziemlich interessant, dass die Python-Methode viel langsamer ist. – GWW

+0

Funktion Anruf Overhead, nehme ich an. – miku

+0

Und es ist interessant, dass mit 'psyco.full()' alle drei Varianten nur etwa 10% der Zeit benötigen. – AndiDog

5

Sie suchen nach der setdefault Methode:

>>> r = {} 
>>> r.setdefault('a', 'b') 
'b' 
>>> r 
{'a': 'b'} 
>>> r.setdefault('a', 'e') 
'b' 
>>> r 
{'a': 'b'} 
+0

+1 für die erste die Frage richtig zu lesen;) – delnan

5

nur einen Punkt zu klären: if key not in d keine lineare Durchsuchung d's Schlüssel tun. Es verwendet die Hash-Tabelle des Diktats, um den Schlüssel schnell zu finden.

+0

Genau das, was ich herausfinden will - ta! –

Verwandte Themen