Les dictionnaires
Also available in presentation mode…
Utilisez in lorsque c'est possible (1)
Bon :
for key in d:
print key
inest généralement plus rapide.- Ce pattern marche aussi pour des items dans des containers arbitraires (comme les listes, les tuples ou les tests).
inest 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
collectionset l'appeler à travers le module :import collections d = collections.defaultdict(...) -
ou importer
defaultdictdirectement :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.

