Python >> Tutoriel Python >  >> Python

python dict:obtenir vs setdefault

Vos deux exemples font la même chose, mais cela ne signifie pas get et setdefault fais.

La différence entre les deux est essentiellement le réglage manuel de d[key] pour pointer vers la liste à chaque fois, contre setdefault réglage automatique de d[key] à la liste uniquement lorsqu'il n'est pas défini.

En rendant les deux méthodes aussi similaires que possible, j'ai couru

from timeit import timeit

print timeit("c = d.get(0, []); c.extend([1]); d[0] = c", "d = {1: []}", number = 1000000)
print timeit("c = d.get(1, []); c.extend([1]); d[0] = c", "d = {1: []}", number = 1000000)
print timeit("d.setdefault(0, []).extend([1])", "d = {1: []}", number = 1000000)
print timeit("d.setdefault(1, []).extend([1])", "d = {1: []}", number = 1000000)

et j'ai

0.794723378711
0.811882272256
0.724429205999
0.722129751973

Alors setdefault est environ 10 % plus rapide que get à cet effet.

Le get méthode vous permet de faire moins que vous pouvez avec setdefault . Vous pouvez l'utiliser pour éviter d'obtenir un KeyError lorsque la clé n'existe pas (si c'est quelque chose qui va arriver fréquemment) même si vous ne voulez pas définir la clé.

Voir Cas d'utilisation pour la méthode dict 'setdefault' et la méthode dict.get() renvoie un pointeur pour plus d'informations sur les deux méthodes.

Le fil de discussion sur setdefault conclut que la plupart du temps, vous voulez utiliser un defaultdict . Le fil de discussion sur get conclut qu'il est lent et qu'il est souvent préférable (en termes de vitesse) de faire une double recherche, d'utiliser un defaultdict ou de gérer l'erreur (selon la taille du dictionnaire et votre cas d'utilisation).


La réponse acceptée d'agf ne compare pas comme avec comme. Après :

print timeit("d[0] = d.get(0, []) + [1]", "d = {1: []}", number = 10000)

d[0] contient une liste de 10 000 éléments alors qu'après :

print timeit("d.setdefault(0, []) + [1]", "d = {1: []}", number = 10000)

d[0] est simplement [] . c'est-à-dire le d.setdefault version ne modifie jamais la liste stockée dans d . Le code devrait en fait être :

print timeit("d.setdefault(0, []).append(1)", "d = {1: []}", number = 10000)

et en fait est plus rapide que le setdefault défectueux exemple.

La différence ici est vraiment due au fait que lorsque vous ajoutez en utilisant la concaténation, la liste entière est copiée à chaque fois (et une fois que vous avez 10 000 éléments, cela commence à devenir mesurable. Utilisation de append les mises à jour de la liste sont amorties en O(1), c'est-à-dire effectivement à temps constant.

Enfin, il existe deux autres options non prises en compte dans la question d'origine :defaultdict ou simplement tester le dictionnaire pour voir s'il contient déjà la clé.

Donc, en supposant d3, d4 = defaultdict(list), {}

# variant 1 (0.39)
d1[key] = d1.get(key, []) + [val]
# variant 2 (0.003)
d2.setdefault(key, []).append(val)
# variant 3 (0.0017)
d3[key].append(val)
# variant 4 (0.002)
if key in d4:
    d4[key].append(val)
else:
    d4[key] = [val]

la variante 1 est de loin la plus lente car elle copie la liste à chaque fois, la variante 2 est la deuxième plus lente, la variante 3 est la plus rapide mais ne fonctionnera pas si vous avez besoin de Python plus ancien que 2.5, et la variante 4 est légèrement plus lente que la variante 3 .

Je dirais utiliser la variante 3 si vous le pouvez, avec la variante 4 en option pour les endroits occasionnels où defaultdict n'est pas un ajustement exact. Évitez vos deux variantes d'origine.


Vous voudrez peut-être regarder defaultdict dans le collections module. Ce qui suit est équivalent à vos exemples.

from collections import defaultdict

data = [('a', 1), ('b', 1), ('b', 2)]

d = defaultdict(list)

for k, v in data:
    d[k].append(v)

Il y a plus ici.