Dans Python 3.3 une classe ChainMap
a été ajoutée au module collections
:
Une classe ChainMap est fournie pour relier rapidement un certain nombre de mappages afin qu'ils puissent être traités comme une seule unité. C'est souvent beaucoup plus rapide que de créer un nouveau dictionnaire et d'exécuter plusieurs appels update ().
Exemple:
>>> from collections import ChainMap
>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = ChainMap(y, x)
>>> for k, v in z.items():
print(k, v)
a 1
c 11
b 10
Il était motivé par ce problème et rendu public par celui-ci (aucun PEP
n'a été créé).
Pour autant que je sache, c'est une alternative à avoir un dictionnaire supplémentaire et à le maintenir avec update()
s.
Les questions sont:
ChainMap
?ChainMap
?Question bonus: existe-t-il un moyen de l'utiliser sur Python2.x?
J'en ai entendu parler dans Transforming Code into Beautiful, Idiomatic Python
Conférence PyCon de Raymond Hettinger et j'aimerais l'ajouter à ma boîte à outils, mais je ne sais pas quand l'utiliser.
J'aime les exemples de @ b4hand, et en effet j'ai utilisé dans le passé des structures de type ChainMap (mais pas ChainMap lui-même) pour les deux objectifs qu'il mentionne: les remplacements de configuration multi-couches et l'émulation de pile/portée variable.
Je voudrais souligner deux autres motivations/avantages/différences de ChainMap
, par rapport à l'utilisation d'une boucle de mise à jour de dict, ne stockant ainsi que la version "finale" ":
Plus d'informations: car une structure ChainMap est "en couches", elle prend en charge la réponse à des questions telles que: ai-je la valeur "par défaut" ou une valeur remplacée? Quelle est la valeur d'origine ("par défaut")? À quel niveau la valeur a-t-elle été remplacée (emprunt à l'exemple de configuration de @ b4hand: user-config ou command-line-overrides)? En utilisant un dict simple, les informations nécessaires pour répondre à ces questions sont déjà perdues.
Compromis de vitesse: supposons que vous ayez N
couches et au plus M
clés dans chacune, la construction d'un ChainMap prend O(N)
et chaque recherche O(N)
pire cas [*], tandis que la construction d'un dict utilisant une boucle de mise à jour prend O(NM)
et chaque recherche O(1)
. Cela signifie que si vous construisez souvent et n'effectuez que quelques recherches à chaque fois, ou si M
est important, l'approche de construction paresseuse de ChainMap fonctionne en votre faveur.
[*] L'analyse en (2) suppose que dict-access est O(1)
, alors qu'en fait c'est O(1)
en moyenne, et O(M)
pire cas. Voir plus de détails ici .
Je pourrais voir utiliser ChainMap
pour un objet de configuration où vous avez plusieurs étendues de configuration comme des options de ligne de commande, un fichier de configuration utilisateur et un fichier de configuration système. Étant donné que les recherches sont classées par ordre dans l'argument constructeur, vous pouvez remplacer les paramètres des étendues inférieures. Je n'ai pas personnellement utilisé ou vu ChainMap
utilisé, mais ce n'est pas surprenant car c'est un ajout assez récent à la bibliothèque standard.
Il peut également être utile pour émuler des cadres de pile dans lesquels vous effectuez un push et un pop des liaisons de variables si vous essayez d'implémenter vous-même une étendue lexicale.
Les documentation de bibliothèque standard pour ChainMap donnent plusieurs exemples et liens vers des implémentations similaires dans des bibliothèques tierces. Plus précisément, il nomme Django classe Context et Enthought classe MultiContext .
Je vais prendre une fissure à ceci:
Chainmap ressemble à une sorte d'abstraction très juste. C'est une bonne solution pour un type de problème très spécialisé. Je propose ce cas d'utilisation.
Si tu as:
Ensuite, vous pouvez envisager d'utiliser un plan de chaîne pour créer une vue sur la collection de mappages.
Mais ce n'est là qu'une justification après coup. Les gars de Python ont eu un problème, ont trouvé une bonne solution dans le contexte de leur code, puis ont fait un travail supplémentaire pour résumer leur solution afin que nous puissions l'utiliser si nous le choisissons. Plus de puissance pour Mais c'est à vous de décider s'il convient à votre problème.
Pour répondre imparfaitement à votre:
Question bonus: existe-t-il un moyen de l'utiliser sur Python2.x?
from ConfigParser import _Chainmap as ChainMap
Cependant gardez à l'esprit que ce n'est pas un vrai ChainMap
, il hérite de DictMixin
et ne définit que:
__init__(self, *maps)
__getitem__(self, key)
keys(self)
# And from DictMixin:
__iter__(self)
has_key(self, key)
__contains__(self, key)
iteritems(self)
iterkeys(self)
itervalues(self)
values(self)
items(self)
clear(self)
setdefault(self, key, default=None)
pop(self, key, *args)
popitem(self)
update(self, other=None, **kwargs)
get(self, key, default=None)
__repr__(self)
__cmp__(self, other)
__len__(self)
Sa mise en œuvre ne semble pas non plus particulièrement efficace.