web-dev-qa-db-fra.com

Comment fonctionne collections.defaultdict?

J'ai lu les exemples dans python docs, mais je ne comprends toujours pas ce que cette méthode signifie. Quelqu'un peut aider? Voici deux exemples tirés de la docs python

>>> 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?

416
Lanston

Habituellement, un dictionnaire Python jette un KeyError si vous essayez d'obtenir un élément avec une clé qui ne figure pas actuellement dans le dictionnaire. La defaultdict, au contraire, créera simplement 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 transmettez au constructeur (plus précisément, il s'agit d'un objet "appelable" arbitraire, qui inclut des objets fonction et type). Pour le premier exemple, les éléments par défaut sont créés à l'aide de int(), qui renvoie l'objet entier 0. Pour le deuxième exemple, les éléments par défaut sont créés à l'aide de list(), qui renvoie un nouvel objet de liste vide.

473
Sven Marnach

defaultdict signifie que si aucune clé n'est trouvée dans le dictionnaire, une nouvelle entrée est créée à la place de KeyError. 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
187
orlp

defaultdict

"Le dictionnaire standard inclut la méthode setdefault () permettant de récupérer une valeur et d’établir une valeur par défaut si celle-ci n’existe pas. Par contre, defaultdict permet à l’appelant de spécifier la valeur par défaut (valeur à retourner) dès que le conteneur est initialisé. "

tel que défini par Doug Hellmann dans Le Python Bibliothèque standard par exemple

Comment utiliser defaultdict

Import defaultdict

>>> from collections import defaultdict

Initialise defaultdict

Initialisez-le 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 fonctionne

Comme c'est une classe enfant du dictionnaire standard, il peut exécuter toutes les mêmes fonctions.

Mais si une clé inconnue est transmise, la valeur par défaut est renvoyée à la place de l'erreur. Pour ex:

>>> d_int['a']
10
>>> d_int['d']
0
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12, 'd': 0})

Si vous souhaitez modifier la valeur par défaut, écrasez 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é avec default_factory, toute clé inconnue retournera 0 par défaut.

Maintenant que la chaîne est passée dans la boucle, le nombre de ces alphabets augmentera 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 en tant que default_factory, toute clé inconnue (non existante) renverra [] (c.-à-d. 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]})
79
Somendra Joshi

Il existe une excellente explication de defaultdicts ici: http://ludovf.net/blog/python-collections-defaultdict/

Fondamentalement, les paramètres int et list sont des fonctions que vous passez. Rappelez-vous que Python accepte les noms de fonction en tant qu'arguments. int renvoie 0 par défaut et list renvoie une liste vide lorsqu'il est appelé avec parenthèses.

Dans les dictionnaires normaux, si dans votre exemple j'essaie d'appeler d[a], j'obtiendrai une erreur (KeyError), car seules les clés m, s, i et p existent et que la clé a n'a pas été initialisée. Mais dans defaultdict, un nom de fonction est utilisé comme argument. Lorsque vous essayez d'utiliser une clé non initialisée, il appelle simplement la fonction que vous avez transmise et assigne sa valeur de retour à la valeur de la nouvelle clé.

18
varagrawal

Les dictionnaires constituent un moyen pratique de stocker des données pour une récupération ultérieure par nom (clé). Les clés doivent être des objets uniques, immuables et sont généralement des chaînes. Les valeurs dans un dictionnaire peuvent être n'importe quoi. Pour de nombreuses applications, les valeurs sont des types simples tels que des entiers et des chaînes.

Cela devient plus intéressant lorsque les valeurs d'un dictionnaire sont des collections (listes, plans, etc.). Dans ce cas, la valeur (liste vide ou dict) doit être initialisée lors de la première utilisation d'une clé donnée. Bien que cela soit relativement facile à faire manuellement, le type defaultdict automatise et simplifie ces types 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 ne lèvera jamais une KeyError. Toute clé inexistante obtient la valeur renvoyée par la fabrique par défaut.

from collections import defaultdict
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 sur l'utilisation de defaultdict pour 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 que la valeur de chaque élément doit commencer par une valeur par défaut, utilisez un defaultdict.

12
dimension

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 être utile pour les cas très complexes.

6
Edward Falk

Puisque la question porte sur "comment ça marche", certains lecteurs voudront peut-être avoir plus de détails. 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) de manière pratique: https://stackoverflow.com/a/17956989/1593924

Pour clarifier ce que "appelable" signifie, voici une session interactive (à partir de la version 2.7.6 mais devrait également fonctionner en v3):

>>> 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})

C'était l'utilisation la plus typique de defaultdict (à l'exception de 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 valeur simple:

>>> 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, ce qui suit fonctionne car il passe dans une fonction simple (il crée à la volée une fonction sans nom qui ne prend aucun argument 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
Jon Coombs

Je pense que sa meilleure utilisation à la place d'une déclaration de cas de commutateur. Imaginez si nous avons une déclaration de cas de commutation 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 d'instructions switch case disponibles en python. Nous pouvons réaliser 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 affiche donc une valeur par défaut que nous avons configurée dans une fonction d'assistance. C'est bien plus intéressant qu'un dictionnaire brut dans lequel une KeyError est levée si la clé n'est pas présente. De ce fait, il est évident que defaultdict ressemble davantage à une instruction de cas de commutation dans laquelle nous pouvons éviter des blocs if-Elif-elif-else compliqués.

Un autre bon exemple qui m'a beaucoup impressionné par 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, nous obtiendrons un compte de 0.

2
Swadhikar C

Sans defaultdict, vous pouvez probablement attribuer de nouvelles valeurs à des clés invisibles, mais vous ne pouvez pas le 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
Ming Liu

Par défaut, defaultdict peut également générer une erreur de clé dans le cas suivant:

    from collections import defaultdict
    d = defaultdict()
    print(d[3]) #raises keyerror

Rappelez-vous toujours de donner un argument à defaultdict comme defaultdict (int).

2
Shweta Sharma

L'outil defaultdict est un conteneur de la classe des collections de Python. Son fonctionnement est similaire à celui du dictionnaire (dict) habituel, mais il y 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

Cela imprime:

('python', ['awesome', 'language'])
('something-else', ['not relevant'])
1
saarthak johari

En bref:

defaultdict(int) - l'argument int indique que les valeurs seront de type int.

defaultdict(list) - la liste d'arguments indique que les valeurs seront du type liste.

1
Shravan kp

Le dictionnaire standard inclut la méthode setdefault () permettant de récupérer une valeur et d’établir une valeur par défaut si cette valeur n’existe pas. Par défaut, defaultdict permet à l'appelant de spécifier la valeur par défaut lors de l'initialisation du conteneur.

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. Cela peut être particulièrement utile si le type par défaut est un type utilisé pour l'agrégation ou l'accumulation de valeurs, telles qu'une liste, un ensemble ou même un entier. La documentation de la bibliothèque standard comprend plusieurs exemples d'utilisation de defaultdict de cette manière.

$ python collections_defaultdict.py

d: defaultdict(<function default_factory at 0x100468c80>, {'foo': 'bar'})
foo => bar
bar => default value
0
user3818875