Comment fait collections.defaultdict travaille?
j'ai lu les exemples dans python docs, mais je n'arrive toujours pas à comprendre ce que cette méthode signifie. Quelqu'un peut-il aider? Voici deux exemples de python docs
>>> 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)]
et
>>> 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])]
les paramètres int
et list
sont pour quoi?
12 réponses
Habituellement, un dictionnaire Python lance un KeyError
si vous essayez d'obtenir un produit avec une clé qui n'est pas actuellement dans le dictionnaire. Le defaultdict
en contraste créera simplement tous les éléments auxquels vous essayez d'accéder (à condition bien sûr qu'ils n'existent pas encore). Pour créer un tel élément" par défaut", il appelle l'objet fonction que vous passez dans le constructeur (plus précisément, c'est un objet arbitraire" appelable", qui inclut les objets fonction et type). Pour le premier exemple, par défaut les éléments sont créés en utilisant int()
, qui retournera l'objet entier 0
. Pour le second exemple, les éléments par défaut sont créés en utilisant list()
, qui renvoie un nouvel objet de liste vide.
defaultdict
signifie que si une clé ne se trouve pas dans le dictionnaire, alors au lieu d'une KeyError
étant lancé, une nouvelle entrée est créée. Le type de cette nouvelle entrée est donné par l'argument de defaultdict.
par exemple:
somedict = {}
print(somedict[3]) # KeyError
someddict = defaultdict(int)
print(someddict[3]) # print int(), thus 0
defaultdict
"Le dictionnaire standard comprend la méthode setdefault() pour récupérer une valeur et d'établir une valeur par défaut si la valeur n'existe pas. Par contre, les defaultdict
permet à l'appelant de spécifier la valeur par défaut(valeur renvoyée) lorsque le conteneur est initialisé."
tel que défini par Doug Hellmann dans the Python Standard Library par exemple
comment utiliser defaultdict
defaultdict D'importation
>>> from collections import defaultdict
Initialiser defaultdict
L'initialiser en passant
callable comme premier argument(obligatoire)
>>> 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 comme deuxième argument(facultatif)
>>> d_int = defaultdict(int, a=10, b=12, c=13)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})
ou
>>> kwargs = {'a':10,'b':12,'c':13}
>>> d_int = defaultdict(int, **kwargs)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})
comment ça marche
comme est une classe d'enfant de dictionnaire standard, il peut effectuer toutes les mêmes fonctions.
mais en cas de passer une clé inconnue il renvoie la valeur par défaut au lieu de l'erreur. Par exemple:
>>> d_int['a']
10
>>> d_int['d']
0
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12, 'd': 0})
dans le cas où vous voulez changer la valeur par défaut overwrite 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})
ou
>>> 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})
exemples dans la Question
exemple 1
comme int a été passé comme default_factory, toute clé inconnue retournera 0 par défaut.
maintenant que la chaîne est passée dans la boucle, elle augmentera le nombre de ces alphabets en 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})
exemple 2
comme une liste a été passée comme default_factory, tout inconnu(inexistante) de la clé sera de retour [ ](ie. liste) par défaut.
maintenant que la liste des tuples est passée dans la boucle, il va ajouter la valeur dans le d [Couleur]
>>> 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]})
il y a une grande explication de defaultdicts ici: http://ludovf.net/blog/python-collections-defaultdict /
Fondamentalement, les paramètres int et liste sont des fonctions que vous passez. Rappelez-vous que Python accepte les noms de fonction comme arguments. int retourne 0 par défaut et list retourne une liste vide lorsqu'on l'appelle avec des parenthèses.
dans les dictionnaires normaux, si dans votre exemple j'essaie d'appeler d[a]
, j'obtiendrai une erreur (KeyError), puisque seules les clés m, s, i et P existent et que la clé a n'a pas été initialisée. Mais dans un defaultdict, il prend un nom de fonction en tant qu'argument, lorsque vous essayez d'utiliser une clé qui n'a pas été initialisé, il appelle simplement la fonction que vous avez passé et assigne sa valeur de retour la valeur de la nouvelle clé.
Puisque la question est "comment ça marche", certains lecteurs veulent voir plus d'écrous et de boulons. Plus précisément, la méthode en question Est la méthode __missing__(key)
. Voir: https://docs.python.org/2/library/collections.html#defaultdict-objects .
Plus concrètement, cette réponse montre comment utiliser __missing__(key)
d'une manière pratique:
https://stackoverflow.com/a/17956989/1593924
à clarifiez ce que "appelable" signifie, Voici une session interactive (à partir de 2.7.6 mais devrait fonctionner dans v3 aussi):
>>> 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})
qui était l'utilisation la plus typique de defaultdict (sauf pour l'utilisation inutile de la variable x). Vous pouvez faire la même chose avec 0 comme valeur par défaut explicite, mais pas avec une simple valeur:
>>> dd2 = defaultdict(0)
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
dd2 = defaultdict(0)
TypeError: first argument must be callable
au lieu de cela, les travaux suivants parce qu'il passe dans une fonction simple (il crée à la volée une fonction sans nom qui ne prend pas arguments et renvoie toujours 0):
>>> dd2 = defaultdict(lambda: 0)
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {})
>>> dd2['a']
0
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {'a': 0})
>>>
et avec une valeur par défaut différente:
>>> dd3 = defaultdict(lambda: 1)
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {})
>>> dd3['a']
1
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {'a': 1})
>>>
mon propre 2¢: Vous pouvez également sous-classe defaultdict:
class MyDict(defaultdict):
def __missing__(self, key):
value = [None, None]
self[key] = value
return value
cela pourrait s'avérer pratique pour les cas très complexes.
sont un moyen pratique de stocker des données pour une extraction ultérieure par nom (clé). Les clés doivent être des objets uniques, immuables, et sont typiquement des chaînes. Les valeurs dans un dictionnaire peut être n'importe quoi. Pour de nombreuses applications, les valeurs sont des types simples tels que des entiers et des chaînes.
Il devient plus intéressant lorsque les valeurs dans un dictionnaire sont des collections (listes, dicts, etc. Dans ce cas, la valeur (une liste vide ou dict) doit être initialisé pour la première fois un étant donné la clé est utilisée. Bien que cela soit relativement facile à faire manuellement, le type defaultdict automatise et simplifie ce genre d'opérations. Un defaultdict fonctionne exactement comme un dict normal, mais il est initialisé avec une fonction ("Default factory") qui ne prend aucun argument et fournit la valeur par défaut pour une clé inexistante.
un defaultdict n'élèvera jamais une KeyError. Toute clé qui n'existe pas obtient la valeur retournée par l'usine par défaut.
from collections import defaultdict
ice_cream = defaultdict(lambda: 'Vanilla')
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
voici un autre exemple comment utiliser defaultdict comment réduire la complexité
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))
en conclusion, chaque fois que vous avez besoin d'un dictionnaire, et la valeur de chaque élément devrait commencer par une valeur par défaut, utilisez un defaultdict.
sans defaultdict
, vous pouvez probablement attribuer de nouvelles valeurs à des clés invisibles, mais vous ne pouvez pas les modifier. Par exemple:
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
je pense qu'il est mieux utilisé à la place d'une déclaration de cas d'interrupteur. Imaginez si nous avons une déclaration de cas d'interrupteur comme ci-dessous:
option = 1
switch(option) {
case 1: print '1st option'
case 2: print '2nd option'
case 3: print '3rd option'
default: return 'No such option'
}
il n'y a pas de case statements switch
disponible en python. Nous pouvons obtenir la même chose en utilisant 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])
il imprime:
Default Value
Default Value
3rd option
dans l'extrait ci-dessus dd
n'a pas de touches 4 ou 5 et donc il imprime une valeur par défaut que nous avons configuré dans une fonction d'assistance. C'est bien plus joli qu'un dictionnaire brut où un KeyError
est lancé si la clé n'est pas présente. De ce fait, il est évident que defaultdict
ressemble plus à une instruction de cas d'interrupteur où nous pouvons éviter un complexe if-elif-elif-else
blocs.
un autre bon exemple qui m'a beaucoup impressionné de ce site est:
>>> 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})
>>>
si nous essayons d'accéder à des éléments autres que eggs
et spam
on aura un compte à 0.
le dictionnaire standard inclut la méthode setdefault() pour extraire une valeur et établir un défaut si la valeur n'existe pas. Par contraste, defaultdict permet à l'appelant de spécifier la valeur par défaut à l'avance lorsque le conteneur est initialisé.
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']
cela fonctionne bien tant qu'il est approprié que toutes les clés aient la même valeur par défaut. Il peut être particulièrement utile si la valeur par défaut est un type utilisé pour agréger ou accumuler des valeurs, comme une liste, set, ou même int. La documentation standard de la bibliothèque comprend plusieurs exemples d'utilisation de defaultdict de cette façon.
$ python collections_defaultdict.py
d: defaultdict(<function default_factory at 0x100468c80>, {'foo': 'bar'})
foo => bar
bar => default value
l'outil defaultdict est un conteneur de la classe collections de Python. Il est similaire au conteneur Dict (dict), mais il a une différence: le type de données des champs de valeur est spécifié lors de l'initialisation.
par exemple:
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
Cette affiche:
('python', ['awesome', 'language'])
('something-else', ['not relevant'])
La documentation et les explications sont assez explicites:
http://docs.python.org/library/collections.html#collections.defaultdict
la fonction type (int/str etc.) passée en argument est utilisé pour initialiser une valeur par défaut pour chaque clé où la clé n'est pas présente dans le dict.