Personal tools
You are here: Home Python Bonnes pratiques et astuces Python Les dictionnaires

Les dictionnaires

by David Goodger last modified May 22, 2010 07:43 PM
Contributors: David Larlet
CC BY-SA

Utilisez in lorsque c'est possible (1)

Bon :

for key in d:
    print key
  • in est généralement plus rapide.
  • Ce pattern marche aussi pour des items dans des containers arbitraires (comme les listes, les tuples ou les tests).
  • in est aussi un opérateur (comme on va le voir).

Mauvais :

for key in d.keys():
    print key

C'est limité aux objects ayant une méthode keys().

Utilisez in lorsque c'est possible (2)

Mais .keys() est nécessaire lorsque vous modifiez le dictionnaire :

for key in d.keys():
    d[str(key)] = d[key]

d.keys() crée une liste statique des clés du dictionnaire. Sinon, vous allez lever une exception "RuntimeError: dictionary changed size during iteration".

Utilisez key in dict, et non dict.has_key() :

# faites ça :
if key in d:
    ...do something with d[key]

# mais pas ça :
if d.has_key(key):
    ...do something with d[key]

in est ici utilisé comme un opérateur.

La méthode get des dictionnaires

On doit souvent initialiser les entrées d'un dictionnaire avant de les utiliser:

Voici la manière naïve de faire :

navs = {}
for (portfolio, equity, position) in data:
    if portfolio not in navs:
        navs[portfolio] = 0
    navs[portfolio] += position * prices[equity]

dict.get(key, default) permet de ne pas avoir à se soucier du test :

navs = {}
for (portfolio, equity, position) in data:
    navs[portfolio] = (navs.get(portfolio, 0)
                       + position * prices[equity])

Beaucoup mieux.

La méthode setdefault des dictionnaires (1)

Ici on doit initialiser les valeurs d'un dictionnaire mutables. Chaque valeur du dictionnaire sera une liste. Voici la manière naïve :

equities = {}
for (portfolio, equity) in data:
    if portfolio in equities:
        equities[portfolio].append(equity)
    else:
        equities[portfolio] = [equity]

dict.setdefault(key, default) s'occupe de ça de manière beaucoup plus rapide :

equities = {}
for (portfolio, equity) in data:
    equities.setdefault(portfolio, []).append(equity)

dict.setdefault() est équivalent à "get ou set & get". Ou "set si nécessaire, puis get". C'est particulièrement rapide si votre clé de dictionnaire est coûteuse à générer ou longue à taper.

Le seul problème avec dict.setdefault() c'est que la valeur par défaut est évaluée, qu'elle soit utilisée ou non. Ça ne pose problème que si la clé est coûteuse à calculer.

Si la valeur par défaut est coûteuse à calculer, vous devriez plutôt utiliser la classe defaultdict.

La méthode setdefault des dictionnaires (2)

On va voir qu'il est possible d'utiliser setdefault pour déclarer une valeur par défaut :

navs = {}
for (portfolio, equity, position) in data:
    navs.setdefault(portfolio, 0)
    navs[portfolio] += position * prices[equity]

La méthode setdefault d'un dictionnaire retourne la valeur par défaut, mais nous l'ignorons ici. On tire profit d'une conséquence de l'utilisation de setdefault, la valeur n'est initialisée que si elle n'existe pas déjà.

defaultdict

Nouveau avec Python 2.5.

defaultdict est nouveau dans Python 2.5, il fait partie du module collections. defaultdict est identique aux dictionnaires classiques, excepté pour deux cas :

  • il prend un premier argument optionnel : une fonction factory par défaut
  • lorsqu'une clé de dictionnaire est rencontrée pour la première fois, la fonction factory par défaut est appelée et le résultat initialise la valeur du dictionnaire.

Il y a deux manières d'accéder à defaultdict :

  • importer le module collections et l'appeler à travers le module :

    import collections
    d = collections.defaultdict(...)
    
  • ou importer defaultdict directement :

    from collections import defaultdict
    d = defaultdict(...)
    

Voici l'exemple déjà traité, où chaque valeur du dictionnaire fois être initialisé pour être une liste vide, réécrit en utilisant defaultdict :

from collections import defaultdict

equities = defaultdict(list)
for (portfolio, equity) in data:
    equities[portfolio].append(equity)

Il n'y a plus d'astuce ici. Dans ce cas, la fonction factory par défaut est list, ce qui retourne une liste vide.

C'est la manière d'avoir un dictionnaire avec les valeurs par défaut à 0, utilisez int comme factory :

navs = defaultdict(int)
for (portfolio, equity, position) in data:
    navs[portfolio] += position * prices[equity]

Il faut faire attention à defaultdict quand même. Vous ne pouvez pas utiliser l'exception KeyError sur un dictionnaire initialisé avec defaultdict. Vous devez utiliser la condition "key in dict" si vous voulez vérifier l'existence d'une clé de manière spécifique.

Construire et scinder des dictionnaires

Voila une technique utile pour construire un dictionnaire à partir de deux listes (ou séquences), une liste pour les clés, une liste pour les valeurs :

given = ['John', 'Eric', 'Terry', 'Michael']
family = ['Cleese', 'Idle', 'Gilliam', 'Palin']
pythons = dict(zip(given, family))
>>> pprint.pprint(pythons)
{'John': 'Cleese',
 'Michael': 'Palin',
 'Eric': 'Idle',
 'Terry': 'Gilliam'}

L'inverse est trivial bien sûr :

 >>> pythons.keys()
 ['John', 'Michael', 'Eric', 'Terry']
 >>> pythons.values()
 ['Cleese', 'Palin', 'Idle', 'Gilliam']

Notez que l'ordre du résultat des .keys() et .values() à est différent des listes utilisées lors de la création du dictionnaire. L'ordre d'entrée est différent de l'ordre de sortie car un dictionnaire n'est pas ordonné. Par contre, l'ordre des clés est consistant avec celui des valeurs, à condition que le dictionnaire n'ait pas été modifié entre temps.

Document Actions