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?

298
demandé sur Brian Burns 2011-05-05 19:45:00

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.

344
répondu Sven Marnach 2016-05-27 12:58:22

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
153
répondu orlp 2015-12-20 14:36:59

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]})
71
répondu Somendra Joshi 2017-05-23 11:54:59

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

17
répondu varagrawal 2014-08-07 20:41:24

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})
>>> 
5
répondu Jon Coombs 2017-05-23 12:18:23

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.

4
répondu Edward Falk 2017-05-15 16:47:58
Les dictionnaires

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.

3
répondu M.r_L 2018-04-23 13:33:28

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
2
répondu Ming Liu 2017-12-19 18:44:44

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.

1
répondu Swadhikar C 2017-08-17 08:49:34

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
0
répondu 2014-12-30 14:05:12

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'])
-1
répondu saarthak johari 2018-03-08 17:59:25

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.

-6
répondu Andreas Jung 2014-06-09 18:09:37