Gérer efficacement plusieurs contextes – Real Python

By | août 2, 2021

trouver un expert Python

Parfois, lorsque vous travaillez avec plusieurs dictionnaires différents, vous devez les regrouper et les gérer comme un seul. Dans d'autres situations, vous pouvez avoir plusieurs dictionnaires représentant différents portées ou alors contextes et devez les traiter comme un dictionnaire unique qui vous permet d'accéder aux données sous-jacentes suivant un ordre ou une priorité donné. Dans ces cas, vous pouvez profiter de Python ChainMap du collections module.

ChainMap regroupe plusieurs dictionnaires et mappages dans une seule vue pouvant être mise à jour avec un comportement semblable à celui d'un dictionnaire. Aditionellement, ChainMap fournit des fonctionnalités qui vous permettent de gérer efficacement divers dictionnaires, de définir des priorités de recherche clés, etc.

Dans ce didacticiel, vous apprendrez à :

  • Créer ChainMap instances dans vos programmes Python
  • Explore le différences entre ChainMap et dict
  • Utilisation ChainMap travailler avec plusieurs dictionnaires en un seul
  • Gérer priorités de recherche clés avec ChainMap

Pour tirer le meilleur parti de ce didacticiel, vous devez connaître les bases de l'utilisation des dictionnaires et des listes en Python.

À la fin du voyage, vous trouverez quelques exemples pratiques qui vous aideront à mieux comprendre les fonctionnalités et les cas d'utilisation les plus pertinents de ChainMap.

Premiers pas avec Python ChainMap

Python ChainMap a été ajouté à collections en Python 3.3 comme un outil pratique pour gérer plusieurs portées et contextes. Cette classe vous permet de regrouper plusieurs dictionnaires et autres mappages pour les faire apparaître logiquement et se comporter comme un seul. Il crée un seul vue actualisable qui fonctionne de la même manière qu'un dictionnaire ordinaire mais avec quelques différences internes.

ChainMap ne fusionne pas ses mappages ensemble. Au lieu de cela, il les conserve dans une liste interne de mappages. Puis ChainMap réimplémente les opérations de dictionnaire communes en haut de cette liste. Étant donné que la liste interne contient des références au mappage d'entrée d'origine, toute modification de ces mappages affecte le ChainMap objet dans son ensemble.

Le stockage des mappages d'entrée dans une liste vous permet d'avoir des clés en double dans une carte de chaîne donnée. Si vous effectuez une recherche de clé, ensuite ChainMap recherche la liste des mappages jusqu'à ce qu'il trouve la première occurrence de la clé cible. Si la clé est manquante, vous obtenez un KeyError comme d'habitude.

Le stockage des mappages dans une liste est vraiment utile lorsque vous devez gérer des étendues imbriquées, où chaque mappage représente une étendue ou un contexte spécifique.

Pour mieux comprendre les portées et les contextes, réfléchissez à la façon dont Python résout les noms. Lorsque Python recherche un nom, il recherche dans des locaux(), globales(), et enfin intégrés jusqu'à ce qu'il trouve la première occurrence du nom cible. Si le nom n'existe pas, vous obtenez un NameError. Traiter des étendues et des contextes est le type de problème le plus courant que vous pouvez résoudre avec ChainMap.

Lorsque vous travaillez avec ChainMap, vous pouvez enchaîner plusieurs dictionnaires avec des clés disjointes ou qui se croisent.

Dans le premier cas, ChainMap vous permet de traiter tous vos dictionnaires comme un seul. Ainsi, vous pouvez accéder aux paires clé-valeur comme si vous travailliez avec un seul dictionnaire. Dans le second cas, en plus de gérer vos dictionnaires comme un seul, vous pouvez également profiter de la liste interne de mappages pour définir une sorte de priorité d'accès pour les touches répétées dans vos dictionnaires. Voilà pourquoi ChainMap les objets sont parfaits pour gérer plusieurs contextes.

Un comportement curieux de ChainMap est-ce mutation, telles que la mise à jour, l'ajout, la suppression, l'effacement et l'éclatement des clés, n'agissent que sur le premier mappage dans la liste interne des mappages. Voici un résumé des principales caractéristiques de ChainMap:

  • Construit un vue actualisable à partir de plusieurs mappages d'entrée
  • Fournit presque le même interface qu'un dictionnaire, mais avec quelques fonctionnalités supplémentaires
  • Ne fusionne pas les mappages d'entrée mais les conserve à la place dans un liste publique interne
  • Voit changements externes dans les mappages d'entrée
  • Peut contenir touches répétées avec des valeurs différentes
  • Recherche les clés de manière séquentielle via la liste interne des mappages
  • Jette un KeyError quand une clé manque après avoir recherché toute la liste des mappages
  • Effectue mutations uniquement sur la première cartographie dans la liste interne

Dans ce tutoriel, vous en apprendrez beaucoup plus sur toutes ces fonctionnalités intéressantes de ChainMap. La section suivante vous guidera dans la création de nouvelles instances de ChainMap dans ton code.

Instanciation ChainMap

Créer ChainMap dans votre code Python, vous devez d'abord importer la classe depuis collections puis appelez-le comme d'habitude. L'initialiseur de classe peut prendre zéro ou plusieurs mappages comme arguments. Sans arguments, il initialise une carte de chaîne avec un dictionnaire vide à l'intérieur :

>>>

>>> de collections importer ChainMap
>>> de collections importer CommandéDict, dict par défaut

>>> # N'utilisez aucun argument
>>> ChainMap()
ChainMap()

>>> # Utilisez des dictionnaires classiques
>>> Nombres = "un": 1, "deux": 2
>>> des lettres = "une": "UNE", "b": "B"

>>> ChainMap(Nombres, des lettres)
ChainMap('un' : 1, 'deux' : 2, 'a' : 'A', 'b' : 'B')

>>> ChainMap(Nombres, "une": "UNE", "b": "B")
ChainMap('un' : 1, 'deux' : 2, 'a' : 'A', 'b' : 'B')

>>> # Utiliser d'autres mappages
>>> Nombres = CommandéDict(un=1, deux=2)
>>> des lettres = dict par défaut(str, "une": "UNE", "b": "B")
>>> ChainMap(Nombres, des lettres)
ChainMap(
                CommandéDict([('one', 1), ('two', 2)]),
                defaultdict(, 'a' : 'A', 'b' : 'B')
)

Ici, vous créez plusieurs ChainMap objets utilisant différentes combinaisons de mappages. Dans chaque cas, ChainMap renvoie une seule vue de type dictionnaire de tous les mappages d'entrée. Notez que vous pouvez utiliser n'importe quel type de mappage, tel que CommandéDict et dict par défaut.

Vous pouvez également créer ChainMap objets utilisant la méthode de classe .fromkeys(). Cette méthode peut prendre un itérable de clés et une valeur par défaut facultative pour toutes les clés :

>>>

>>> de collections importer ChainMap

>>> ChainMap.à partir des clés([[[["un", "deux","Trois"])
ChainMap('un' : aucun, 'deux' : aucun, 'trois' : aucun)

>>> ChainMap.à partir des clés([[[["un", "deux","Trois"], 0)
ChainMap('un' : 0, 'deux' : 0, 'trois' : 0)

Si vous appelez .fromkeys() sur ChainMap avec un itérable de clés comme argument, vous obtenez alors une carte de chaîne avec un seul dictionnaire. Les clés proviennent de l'itérable d'entrée et les valeurs par défaut sont Rien. Facultativement, vous pouvez passer un deuxième argument à .fromkeys() pour fournir une valeur par défaut raisonnable pour chaque clé.

Exécution d'opérations de type dictionnaire

ChainMap prend en charge la même API que les dictionnaires classiques pour accéder aux clés existantes. Une fois que vous avez un ChainMap objet, vous pouvez récupérer des clés existantes avec une recherche de clé de style dictionnaire, ou vous pouvez utiliser .avoir():

>>>

>>> de collections importer ChainMap

>>> Nombres = "un": 1, "deux": 2
>>> des lettres = "une": "UNE", "b": "B"

>>> alpha_num = ChainMap(Nombres, des lettres)
>>> alpha_num[[[["deux"]
2

>>> alpha_num.avoir("une")
'UNE'

>>> alpha_num[[[["Trois"]
Traceback (appel le plus récent en dernier) :
    ...
KeyError: 'Trois'

Une recherche de clé recherche tous les mappages dans la carte de chaîne cible jusqu'à ce qu'elle trouve la clé souhaitée. Si la clé n'existe pas, alors vous obtenez l'habituel KeyError. Maintenant, comment se comporte une opération de recherche lorsque vous avez des clés en double ? Dans ce cas, vous obtenez la première occurrence de la clé cible :

>>>

>>> de collections importer ChainMap

>>> pour_adoption = "chiens": dix, "chats": 7, "pythons": 3
>>> vet_traitement = "chiens": 4, "chats": 3, "tortues": 1
>>> animaux domestiques = ChainMap(pour_adoption, vet_traitement)

>>> animaux domestiques[[[["chiens"]
dix
>>> animaux domestiques.avoir("chats")
7
>>> animaux domestiques[[[["tortues"]
1

Lorsque vous accédez à une clé en double, telle que "chiens" et "chats", la carte de chaîne ne renvoie que la première occurrence de cette clé. En interne, les opérations de recherche recherchent les mappages d'entrée dans le même ordre qu'ils apparaissent dans la liste interne des mappages, qui est également l'ordre exact dans lequel vous les passez dans l'initialiseur de la classe.

Ce comportement général s'applique également à l'itération :

>>>

>>> de collections importer ChainMap

>>> pour_adoption = "chiens": dix, "chats": 7, "pythons": 3
>>> vet_traitement = "chiens": 4, "chats": 3, "tortues": 1
>>> animaux domestiques = ChainMap(pour_adoption, vet_traitement)

>>> pour clé, valeur dans animaux domestiques.éléments():
...     imprimer(clé, "->", valeur)
...
chiens -> 10
chats -> 7
tortues -> 1
pythons -> 3

Le pour boucle parcourt les dictionnaires dans animaux domestiques et imprime la première occurrence de chaque paire clé-valeur. Vous pouvez également parcourir le dictionnaire directement ou avec .clés() et .valeurs() comme vous pouvez le faire avec n'importe quel dictionnaire :

>>>

>>> de collections importer ChainMap

>>> pour_adoption = "chiens": dix, "chats": 7, "pythons": 3
>>> vet_traitement = "chiens": 4, "chats": 3, "tortues": 1
>>> animaux domestiques = ChainMap(pour_adoption, vet_traitement)

>>> pour clé dans animaux domestiques:
...     imprimer(clé, "->", animaux domestiques[[[[clé])
...
chiens -> 10
chats -> 7
tortues -> 1
pythons -> 3

>>> pour clé dans animaux domestiques.clés():
...     imprimer(clé, "->", animaux domestiques[[[[clé])
...
chiens -> 10
chats -> 7
tortues -> 1
pythons -> 3

>>> pour valeur dans animaux domestiques.valeurs():
...     imprimer(valeur)
...
dix
7
1
3

Encore une fois, le comportement est le même. Chaque itération passe par la première occurrence de chaque clé, élément et valeur dans la carte de chaîne sous-jacente.

ChainMap prend également en charge mutation. En d'autres termes, il vous permet de mettre à jour, d'ajouter, de supprimer et de faire apparaître des paires clé-valeur. La différence dans ce cas est que ces opérations n'agissent que sur le premier mappage :

>>>

>>> de collections importer ChainMap

>>> Nombres = "un": 1, "deux": 2
>>> des lettres = "une": "UNE", "b": "B"

>>> alpha_num = ChainMap(Nombres, des lettres)
>>> alpha_num
ChainMap('un' : 1, 'deux' : 2, 'a' : 'A', 'b' : 'B')

>>> # Ajouter une nouvelle paire clé-valeur
>>> alpha_num[[[["c"] = "C"
>>> alpha_num
ChainMap('un': 1, 'deux': 2, 'c': 'C', 'a': 'A', 'b': 'B')

>>> # Mettre à jour une clé existante
>>> alpha_num[[[["b"] = "b"
>>> alpha_num
ChainMap('un': 1, 'deux': 2, 'c': 'C', 'b': 'b', 'a': 'A', 'b': 'B')

>>> # Touches pop
>>> alpha_num.pop("deux")
2
>>> alpha_num.pop("une")
Traceback (appel le plus récent en dernier) :
    ...
KeyError: "Clé introuvable dans le premier mappage : 'a'"

>>> # Supprimer les clés
>>> del alpha_num[[[["c"]
>>> alpha_num
ChainMap('un': 1, 'b': 'b', 'a': 'A', 'b': 'B')
>>> del alpha_num[[[["une"]
Traceback (appel le plus récent en dernier) :
    ...
KeyError: "Clé introuvable dans le premier mappage : 'a'"

>>> # Effacer le dictionnaire
>>> alpha_num.dégager()
>>> alpha_num
ChainMap(, 'a' : 'A', 'b' : 'B')

Les opérations qui modifient le contenu d'une carte de chaîne donnée n'affectent que le premier mappage, même si la clé que vous essayez de muter existe dans d'autres mappages de la liste. Par exemple, lorsque vous essayez de mettre à jour "b" dans le deuxième mappage, ce qui se passe réellement, c'est que vous ajoutez une nouvelle clé au premier dictionnaire.

Vous pouvez tirer parti de ce comportement pour créer des cartes de chaîne pouvant être mises à jour qui ne modifient pas vos dictionnaires d'entrée d'origine. Dans ce cas, vous pouvez utiliser un dictionnaire vide comme premier argument pour ChainMap:

>>>

>>> de collections importer ChainMap

>>> Nombres = "un": 1, "deux": 2
>>> des lettres = "une": "UNE", "b": "B"

>>> alpha_num = ChainMap(, Nombres, des lettres)
>>> alpha_num
ChainMap(, 'un' : 1, 'deux' : 2, 'a' : 'A', 'b' : 'B')

>>> alpha_num[[[["virgule"] = ","
>>> alpha_num[[[["point final"] = "."

>>> alpha_num
ChainMap(
                'virgule' : ',', 'point' : '.',
                'un' : 1, 'deux' : 2,
                'a' : 'A', 'b' : 'B'
)

Ici, vous utilisez un dictionnaire vide () créer alpha_num. Cela garantit que les modifications que vous effectuez sur alpha_num n'affectera jamais vos deux dictionnaires d'entrée d'origine, Nombres et des lettres, et n'affectera que le dictionnaire vide au début de la liste.

Fusionner ou enchaîner les dictionnaires

Comme alternative à l'enchaînement de plusieurs dictionnaires avec ChainMap, vous pouvez envisager de les fusionner en utilisant dict.update():

>>>

>>> de collections importer ChainMap

>>> # Dictionnaires en chaîne avec ChainMap
>>> pour_adoption = "chiens": dix, "chats": 7, "pythons": 3
>>> vet_traitement = "hamsters": 2, "tortues": 1

>>> ChainMap(pour_adoption, vet_traitement)
ChainMap(
                'chiens' : 10, 'chats' : 7, 'pythons' : 3,
                'hamsters' : 2, 'tortues' : 1
)

>>> # Fusionner les dictionnaires avec .update()
>>> animaux domestiques = 
>>> animaux domestiques.mettre à jour(pour_adoption)
>>> animaux domestiques.mettre à jour(vet_traitement)
>>> animaux domestiques
'chiens': 10, 'chats': 7, 'pythons': 3, 'hamsters': 2, 'tortues': 1

Dans cet exemple spécifique, vous obtenez des résultats similaires lorsque vous créez une carte de chaîne et un dictionnaire équivalent à partir de deux dictionnaires existants avec des clés uniques.

Fusionner des dictionnaires avec .mettre à jour() a des avantages et des inconvénients par rapport à les enchaîner avec ChainMap. Le premier et le plus important inconvénient est que vous supprimez la possibilité de gérer et de hiérarchiser l'accès aux clés répétées à l'aide de plusieurs portées ou contextes. Avec .mettre à jour(), la dernière valeur que vous fournissez pour une clé donnée prévaudra toujours :

>>>

>>> pour_adoption = "chiens": dix, "chats": 7, "pythons": 3
>>> vet_traitement = "chats": 2, "chiens": 1

>>> # Fusionner les dictionnaires avec .update()
>>> animaux domestiques = 
>>> animaux domestiques.mettre à jour(pour_adoption)
>>> animaux domestiques.mettre à jour(vet_traitement)
>>> animaux domestiques
'chiens' : 1, 'chats' : 2, 'pythons' : 3

Les dictionnaires normaux ne peuvent pas stocker de clés répétées. Chaque fois que vous appelez .mettre à jour() avec une valeur pour une clé existante, cette clé est mise à jour avec la nouvelle valeur. Dans ce cas, vous perdez la possibilité de hiérarchiser l'accès aux clés en double en utilisant différentes étendues.

Supposons maintenant que vous ayez m mappages différents avec au plus m clés chacun. Créer un ChainMap objet d'eux prendrait O(m) temps d'exécution, alors que la récupération d'une clé prendrait O(m) dans le pire des cas, dans lequel la clé cible se trouve dans le dernier dictionnaire de la liste interne des mappages.

Alternativement, créer un dictionnaire régulier en utilisant .mettre à jour() en boucle prendrait O(nm), alors que récupérer une clé du dictionnaire final prendrait O(1).

La conclusion est que, si vous créez souvent des chaînes de dictionnaires et n'effectuez que quelques recherches de clé à chaque fois, vous devez utiliser ChainMap. Si c'est l'inverse, utilisez des dictionnaires classiques, sauf si vous avez besoin de clés en double ou de plusieurs portées.

Une autre différence entre la fusion et le chaînage des dictionnaires est que lorsque vous utilisez ChainMap, les modifications externes dans les dictionnaires d'entrée affectent la chaîne sous-jacente, ce qui n'est pas le cas avec les dictionnaires fusionnés.

Exploration des fonctionnalités supplémentaires de ChainMap

ChainMap fournit principalement la même API et les mêmes fonctionnalités qu'un dictionnaire Python classique, avec quelques différences subtiles que vous connaissez déjà. ChainMap prend également en charge certaines fonctionnalités supplémentaires spécifiques à sa conception et à ses objectifs.

Dans cette section, vous découvrirez toutes ces fonctionnalités supplémentaires. Vous apprendrez comment ils peuvent vous aider à gérer différentes portées et contextes lorsque vous accédez aux paires clé-valeur dans vos dictionnaires.

Gérer la liste des mappages avec .Plans

ChainMap stocke tous les mappages d'entrée dans une liste interne. Cette liste est accessible via un attribut d'instance publique appelé .Plans, et il peut être mis à jour par l'utilisateur. L'ordre des mappages dans .Plans correspond à l'ordre dans lequel vous les transmettez ChainMap. Cet arrêté définit le ordre de recherche lorsque vous effectuez des opérations de recherche de clé.

Voici un exemple de la façon dont vous pouvez accéder .Plans:

>>>

>>> de collections importer ChainMap

>>> pour_adoption = "chiens": dix, "chats": 7, "pythons": 3
>>> vet_traitement = "chiens": 4, "tortues": 1

>>> animaux domestiques = ChainMap(pour_adoption, vet_traitement)
>>> animaux domestiques.Plans
['dogs': 10, 'cats': 7, 'pythons': 3, 'dogs': 4, 'turtles': 1]

Ici, vous utilisez .Plans pour accéder à la liste interne des mappages qui animaux domestiques tient. Cette liste est une liste Python standard, vous pouvez donc ajouter et supprimer des mappages manuellement, parcourir la liste, modifier l'ordre des mappages, et plus encore :

>>>

>>> de collections importer ChainMap

>>> pour_adoption = "chiens": dix, "chats": 7, "pythons": 3
>>> vet_traitement = "chats": 1
>>> animaux domestiques = ChainMap(pour_adoption, vet_traitement)

>>> animaux domestiques.Plans.ajouter("hamsters": 2)
>>> animaux domestiques.Plans
['dogs': 10, 'cats': 7, 'pythons': 3, "cats": 1, 'hamsters': 2]

>>> del animaux domestiques.Plans[[[[1]
>>> animaux domestiques.Plans
['dogs': 10, 'cats': 7, 'pythons': 3, 'hamsters': 2]

>>> pour cartographie dans animaux domestiques.Plans:
...     imprimer(cartographie)
...
'chiens': 10, 'chats': 7, 'pythons': 3
'hamster': 2

Dans ces exemples, vous ajoutez d'abord un nouveau dictionnaire à .Plans en utilisant .ajouter(). Ensuite, vous utilisez le del mot-clé pour supprimer le dictionnaire à la position 1. Vous pouvez gérer .Plans comme vous le feriez pour n'importe quelle liste Python ordinaire.

Vous pouvez utiliser .Plans pour itérer sur tous vos mappages pendant que vous effectuez des actions sur eux. La possibilité d'itérer dans la liste des mappages vous permet d'effectuer différentes actions sur chaque mappage. Avec cette option, vous pouvez contourner le comportement par défaut consistant à muter uniquement le premier mappage de la liste.

Un exemple intéressant est que vous pouvez inverser l'ordre de la liste actuelle des mappages en utilisant .sens inverse():

>>>

>>> de collections importer ChainMap

>>> pour_adoption = "chiens": dix, "chats": 7, "pythons": 3
>>> vet_traitement = "chats": 1
>>> animaux domestiques = ChainMap(pour_adoption, vet_traitement)
>>> animaux domestiques
ChainMap('chiens' : 10, 'chats' : 7, 'pythons' : 3, 'chats' : 1)

>>> animaux domestiques.Plans.sens inverse()
>>> animaux domestiques
ChainMap('chats' : 1, 'chiens' : 10, 'chats' : 7, 'pythons' : 3)

L'inversion de la liste interne des mappages vous permet d'inverser l'ordre de recherche lorsque vous recherchez une clé donnée dans la carte de la chaîne. Maintenant, quand tu cherches "chats", vous obtenez le nombre de chats sous traitement vétérinaire au lieu des chats prêts à être adoptés.

Ajout de nouveaux sous-contextes avec .new_child()

ChainMap met également en œuvre .new_child(). Cette méthode prend éventuellement un mappage comme argument et renvoie un nouveau ChainMap instance contenant le mappage d'entrée suivi de tous les mappages actuels dans le mappage de chaîne sous-jacent :

>>>

>>> de collections importer ChainMap

>>> maman = "Nom": "Jeanne", "âge": 31
>>> papa = "Nom": "John", "âge": 35

>>> famille = ChainMap(maman, papa)
>>> famille
ChainMap('nom': 'Jane', 'âge': 31, 'nom': 'Jean', 'âge': 35)

>>> fils = "Nom": "Mike", "âge": 0
>>> famille = famille.nouveau_enfant(fils)

>>> pour personne dans famille.Plans:
...     imprimer(personne)
...
'nom' : 'Mike', 'âge' : 0
'nom' : 'Jeanne', 'âge' : 31
'nom' : 'Jean', 'âge' : 35

Ici, .new_child() renvoie un nouveau ChainMap objet contenant un nouveau mapping, fils, suivi des anciens mappages, maman et papa. Notez que le nouveau mappage occupe désormais la première position dans la liste interne des mappages, .Plans.

Avec .new_child(), vous pouvez créer un sous-contexte que vous pouvez mettre à jour sans modifier aucun des mappages existants. Par exemple, si vous appelez .new_child() sans argument, alors il utilise un dictionnaire vide et le place au début de .Plans. Après cela, vous pouvez effectuer toutes les mutations sur votre nouveau mappage vide, en gardant le reste du mappage en lecture seule.

Sauter les sous-contextes avec .Parents

Une autre caractéristique intéressante de ChainMap est .Parents. Cette propriété renvoie un nouveau ChainMap instance avec tous les mappages dans la carte de chaîne sous-jacente, à l'exception du premier. Cette fonctionnalité est utile pour ignorer le premier mappage lorsque vous recherchez des clés dans une carte de chaîne donnée :

>>>

>>> de collections importer ChainMap

>>> maman = "Nom": "Jeanne", "âge": 31
>>> papa = "Nom": "John", "âge": 35
>>> fils = "Nom": "Mike", "âge":  0

>>> famille = ChainMap(fils, maman, papa)
>>> famille
ChainMap(
                'nom' : 'Mike', 'âge' : 0,
                'nom' : 'Jeanne', 'âge' : 31,
                'nom' : 'Jean', 'âge' : 35
)

>>> famille.Parents
ChainMap('nom': 'Jane', 'âge': 31, 'nom': 'Jean', 'âge': 35)

Dans cet exemple, vous utilisez .Parents pour sauter le premier dictionnaire contenant les données du fils. Dans un sens, .Parents fait l'inverse de .new_child(). Le premier supprime un dictionnaire, tandis que le second ajoute un nouveau dictionnaire au début de la liste. Dans les deux cas, vous obtenez une nouvelle carte de chaîne.

Gérer les étendues et les contextes avec ChainMap

On peut dire que le cas d'utilisation principal de ChainMap est de fournir un moyen efficace de gérer plusieurs portées ou alors contextes et manier priorités d'accès pour les clés en double. Cette fonctionnalité est utile lorsque vous disposez de plusieurs dictionnaires qui stockent des clés en double et que vous souhaitez définir l'ordre dans lequel votre code y accédera.

Dans le ChainMap documentation, vous trouverez un exemple classique qui émule la façon dont Python résout les noms de variables dans les différents espaces de noms.

Lorsque Python recherche un nom, il recherche les portées locale, globale et intégrée de manière séquentielle, en suivant le même ordre jusqu'à ce qu'il trouve le nom cible. Les portées Python sont des dictionnaires qui mappent des noms à des objets.

Pour émuler la chaîne de recherche interne de Python, vous pouvez utiliser une carte de chaîne :

>>>

>>> importer intégrés

>>> # Entrée Shadow avec un nom global
>>> saisir = 42

>>> pylookup = ChainMap(des locaux(), mondiales(), vars(intégrés))

>>> # Récupérer l'entrée de l'espace de noms global
>>> pylookup[[[["saisir"]
42

>>> # Supprimer l'entrée de l'espace de noms global
>>> del mondiales()[[[["saisir"]

>>> # Récupérer l'entrée de l'espace de noms intégré
>>> pylookup[[[["saisir"]

Dans cet exemple, vous créez d'abord une variable globale appelée saisir qui fait de l'ombre à l'intégré saisir() fonction dans le intégrés portée. Ensuite tu crées pylookup comme une carte de chaîne contenant les trois dictionnaires qui contiennent chaque portée Python.

Lorsque vous récupérez saisir de pylookup, vous obtenez la valeur 42 de la portée mondiale. Si vous supprimez le saisir clé de la globales() dictionnaire et accédez-y à nouveau, alors vous obtenez le saisir() fonction de la intégrés scope, qui a la priorité la plus basse dans la chaîne de recherche de Python.

De même, vous pouvez utiliser ChainMap pour définir et gérer l'ordre de recherche des clés en double. Cela vous permet de prioriser l'accès à l'instance souhaitée d'une clé en double.

Suivi ChainMap dans la bibliothèque standard

L'origine de ChainMap est étroitement liée à un problème de performance dans ConfigParser, qui vit dans le analyseur de configuration module dans la bibliothèque standard. Avec ChainMap, les principaux développeurs Python ont considérablement amélioré les performances de ce module dans son ensemble en optimisant la mise en œuvre de ConfigParser.get().

Vous pouvez également trouver ChainMap dans le cadre de Modèle dans le chaîne de caractères module. Cette classe prend un modèle de chaîne comme argument et vous permet d'effectuer des substitutions de chaîne comme décrit dans PEP 292. Le modèle de chaîne d'entrée contient des identifiants intégrés que vous pouvez ultérieurement remplacer par des valeurs réelles :

>>>

>>> importer chaîne de caractères

>>> salutation = "Hé $name, bienvenue à $place !"
>>> modèle = chaîne de caractères.Modèle(salutation)

>>> modèle.remplacer("Nom": "Jeanne", "endroit": "le monde")
« Hé Jane, bienvenue dans le monde ! »

Lorsque vous fournissez des valeurs pour Nom et endroit grâce à un dictionnaire, .remplacer() les remplace dans la chaîne du modèle. Aditionellement, .remplacer() peut prendre des valeurs comme arguments de mot-clé (**kwargs), ce qui peut provoquer des collisions de noms dans certaines situations :

>>>

>>> importer chaîne de caractères

>>> salutation = "Hé $name, bienvenue à $place !"
>>> modèle = chaîne de caractères.Modèle(salutation)

>>> modèle.remplacer(
...     "Nom": "Jeanne", "endroit": "le monde",
...     endroit="Vrai Python"
... )
« Hé Jane, bienvenue dans Real Python ! »

Dans cet exemple, .remplacer() remplace endroit avec la valeur que vous fournissez comme argument de mot-clé au lieu de la valeur dans le dictionnaire d'entrée. Si vous creusez un peu dans le code de cette méthode, vous verrez qu'elle utilise ChainMap pour gérer efficacement la priorité des valeurs d'entrée lorsqu'une collision de noms se produit.

Voici un fragment de code source de .remplacer():

# chaîne.py
# Snip...
de collections importer ChainMap comme _ChainMap

_sentinelle_dict = 

classer Modèle:
    """Une classe de chaîne pour prendre en charge les $-substitutions."""
    # Snip...

    déf remplacer(soi, cartographie=_sentinelle_dict, /, **kws):
        si cartographie est _sentinelle_dict:
            cartographie = kws
        elif kws:
            cartographie = _ChainMap(kws, cartographie)
        # Snip...

Ici, la ligne en surbrillance fait la magie. Il utilise une carte de chaîne qui prend deux dictionnaires, kws et cartographie, comme arguments. En plaçant kws comme premier argument, la méthode définit la priorité des identifiants en double dans les données d'entrée.

Mettre Python ChainMap En action

Jusqu'à présent, vous avez appris à utiliser ChainMap pour travailler avec plusieurs dictionnaires comme un seul. Vous avez également découvert les fonctionnalités de ChainMap et à quel point cette classe est différente des dictionnaires classiques. Les cas d'utilisation de ChainMap sont assez spécifiques. Ils comprennent:

  • Regrouper plusieurs dictionnaires dans un vue unique efficacement
  • Recherche dans plusieurs dictionnaires avec un certain priorité
  • Fournir une chaîne de les valeurs par défaut et gérer leur priorité
  • Améliorer les performances du code qui calcule fréquemment sous-ensembles d'un dictionnaire

Dans cette section, vous allez coder quelques exemples pratiques qui vous aideront à avoir une meilleure idée de la façon d'utiliser ChainMap pour résoudre des problèmes du monde réel.

Accéder à plusieurs inventaires en un seul

Le premier exemple que vous coderez utilise ChainMap to search multiple dictionaries in a single view efficiently. In this case, you would assume you have a bunch of independent dictionaries with unique keys across them.

Say you’re running a store that sells fruits and vegetables. You’ve coded a Python application to manage your inventories. The application reads from a database and returns two dictionaries containing data about the prices of fruits and vegetables, respectively. You need an efficient way to group and manage this data in a single dictionary.

After some research, you end up using ChainMap:

>>>

>>> from collections import ChainMap

>>> fruits_prices = "apple": 0.80, "grape": 0.40, "orange": 0.50
>>> veggies_prices = "tomato": 1.20, "pepper": 1.30, "onion": 1.25
>>> prices = ChainMap(fruits_prices, veggies_prices)

>>> order = "apple": 4, "tomato": 8, "orange": 4

>>> for product, units in order.items():
...     price = prices[[[[product]
...     subtotal = units * price
...     imprimer(f"product:6: $price:.2f    × units    = $subtotal:.2f")
...
apple : $0.80 × 4 = $3.20
tomato: $1.20 × 8 = $9.60
orange: $0.50 × 4 = $2.00

In this example, you use a ChainMap to create a single dictionary-like object that groups data from fruits_prices et veggies_prices. This allows you to access the underlying data as if you effectively had a single dictionary. Le for loop iterates over the products in a given order. Then it computes the subtotal to pay per type of product and prints it on your screen.

You might think to group the data in a new dictionary, using .update() in a loop. This could work just fine when you have a limited variety of products and a small inventory. However, if you manage many products of different types, then using .update() to build a new dictionary could be inefficient compared with ChainMap.

Using ChainMap to solve this kind of problem can also help you define priorities for products of different batches, allowing you to manage your inventories in a First-In/First-Out (FIFO) fashion.

Prioritizing Command-Line Apps Settings

ChainMap is especially helpful in managing default configuration values in your applications. As you already know, one of the main features of ChainMap is that it allows you to set priorities for key lookup operations. This sounds like the right tool for solving the problem of managing configurations in your applications.

For example, say you’re working on a command-line interface (CLI) application. The application allows the user to specify a proxy service for connecting to the Internet. The settings priorities are:

  1. Command-line options (--proxy, -p)
  2. Local configuration files in the user’s home directory
  3. System-wide proxy configuration

If the user supplies a proxy at the command line, then the application must use that proxy. Otherwise, the application should use the proxy provided in the next configuration object, and so on. This is one of the most common use cases of ChainMap. In this situation, you can do the following:

>>>

>>> from collections import ChainMap

>>> cmd_proxy =   # The user doesn't provide a proxy
>>> local_proxy = "proxy": "proxy.local.com"
>>> system_proxy = "proxy": "proxy.global.com"

>>> config = ChainMap(cmd_proxy, local_proxy, system_proxy)
>>> config[[[["proxy"]
'proxy.local.com'

ChainMap allows you to define the appropriate priority for the application’s proxy configuration. A key lookup searches cmd_proxy, then local_proxy, and finally system_proxy, returning the first instance of the key at hand. In the example, the user doesn’t provide a proxy at the command line, so the application takes the proxy from local_proxy, which is the next settings provider in the list.

Managing Default Argument Values

Another use case of ChainMap is to manage default argument values in your methods and functions. Say you’re coding an application to manage the data about the employees in your company. You have the following class, which represents a generic user:

class User:
    def __init__(self, Nom, user_id, role):
        self.Nom = Nom
        self.user_id = user_id
        self.role = role

    # Snip...

At some point, you need to add a feature that allows employees to access different components of a CRM system. Your first thought is to modify User to add the new functionality. However, that could make the class too complex, so you decide to create a subclass, CRMUser, to provide the required functionality.

The class will take a user Nom and CRM component as arguments. It’ll also take some **kwargs. You want to implement CRMUser in a way that allows you to provide sensible default values to the base class’s initializer without losing the flexibility of **kwargs.

Here’s how you solve the problem using ChainMap:

from collections import ChainMap

class CRMUser(User):
    def __init__(self, Nom, component, **kwargs):
        defaults = "user_id": next(component.user_id), "role": "read"
        super().__init__(Nom, **ChainMap(kwargs, defaults))

In this code sample, you create a subclass of User. In the class initializer, you take Nom, component, and **kwargs as arguments. Then you create a local dictionary with default values for user_id et role. Then you call the parent class’s .__init__() method using super(). In this call, you pass Nom directly to the parent’s initializer and provide the default values to the rest of the arguments using a chain map.

Note that the ChainMap object takes kwargs and then defaults as arguments. This order guarantees that manually-provided arguments (kwargs) take precedence over defaults values when you instantiate the class.

Conclusion

Python’s ChainMap du collections module provides an efficient tool for managing several dictionaries as a single one. This class is handy when you have multiple dictionaries representing different scopes ou alors contexts and need to set access priorities to the underlying data.

ChainMap groups multiple dictionaries and mappings in an updatable view that works pretty much like a dictionary. You can use ChainMap objects to efficiently work with several dictionaries, define key lookup priorities, and manage multiple contexts in Python.

In this tutorial, you learned how to:

  • Create ChainMap instances in your Python programs
  • Explore the differences between ChainMap et dict
  • Manage several dictionaries as one using ChainMap
  • Set priorities for key lookup operations with ChainMap

In this tutorial, you also coded a few practical examples that helped you better understand when and how to use ChainMap in your Python code.

[ad_2]