PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Mise à jour

Mise à jour de la base de données, veuillez patienter...

Chercher dans plusieurs dicts à la fois avec ChainMap

vendredi 11 juillet 2014 à 10:09

Depuis Python 3.3 existe un nouvel outil pour travailler avec les dicos et j’étais complètement passé à côté : ChainMap.

Il permet de créer un objet qui se comporte comme un dict, mais qui va chercher dans plusieurs dictionnaires.

Un exemple sera plus clair.

Imaginez que vous ayez un système de configuration avec des valeurs par défaut :

default_config = {'DEBUG': False, 'HOST': 'localhost', 'PORT': 8080}

Puis votre utilisateur peut fournir un fichier de configuration settings.py qui contient :

DEBUG = True
PORT = 8000

Et avec un peu de parsing, vous le récupérez sous forme de dico :

import settings
user_config = {k: v for k, v in vars(settings).items() if k.isupper()}
## {'DEBUG': True, 'PORT': 8000}

Puis l’utilisateur peut passer la config via la ligne de commande, et une fois il fait :

--host 0.0.0.0

Et vous récupérez la config :

cmd_config = {"HOST": "0.0.0.0"}

Maintenant il faut prendre tout ça en compte. La ligne de commande écrase le fichier de config qui écrase les valeurs par défaut :

conf = {}
conf.update(default_config)
conf.update(user_config)
conf.update(cmd_config)
print(conf) # configuration finale
## {'DEBUG': True, 'HOST': '0.0.0.0', 'PORT': 8000}

Ça va marcher, mais ça a plusieurs défauts :

ChainMap résout ce problème en cherchant une clé dans une liste de dicos sous-jacent, mais en appliquant les modifications uniquement sur le premier dico.

>>> from collections import ChainMap
 
>>> conf = ChainMap({}, # <- ce mapping sera le seul modifié
                    # les clés seront cherchées dans cet ordre :
                    cmd_config, 
                    user_config, 
                    default_config)
 
>>> conf['HOST']
>>> '0.0.0.0'
>>> conf['DEBUG']
>>> True
>>> conf['PORT']
>>> 8000

Les dicos sont ici stockés par référence, ça ne prend pas de mémoire en plus, et si on modifie un dico :

user_config['DEBUG'] = False

Alors c’est reflété par ChainMap:

>>> conf['DEBUG']
False

Si on fait une modification, seul le dico le plus haut dans la chaine (ici notre dico vide), est modifié :

>>> conf["PORT"] = 7777
>>> conf
>>> ChainMap({'PORT': 7777}, {'HOST': '0.0.0.0'}, {'DEBUG': False, 'PORT': 8000}, {'DEBUG': False, 'HOST': 'localhost', 'PORT': 8080})

Et si on a besoin d’un contexte temporaire, on peut créer un enfant :

>>> sub_conf = conf.new_child()
>>> sub_conf
ChainMap({}, {'PORT': 7777}, {'HOST': '0.0.0.0'}, {'DEBUG': False, 'PORT': 8000}, {'DEBUG': False, 'HOST': 'localhost', 'PORT': 8080})

Cela crée un nouveau ChainMap, avec un dico vide en haut de la chaîne, qui permet donc de travailler temporairement avec de nouvelles valeurs, sans toucher au ChainMap d’origine.

flattr this!

Error happened! 0 - count(): Argument #1 ($value) must be of type Countable|array, null given In: /var/www/ecirtam.net/autoblogs/autoblogs/autoblog.php:428 http://ecirtam.net/autoblogs/autoblogs/sametmaxcom_a844ada43a979e3b1395ab9acb6afafb84340999/?Chercher-dans-plusieurs-dicts-%C3%A0-la-fois-avec-ChainMap #0 /var/www/ecirtam.net/autoblogs/autoblogs/autoblog.php(999): VroumVroum_Blog->update() #1 /var/www/ecirtam.net/autoblogs/autoblogs/sametmaxcom_a844ada43a979e3b1395ab9acb6afafb84340999/index.php(1): require_once('...') #2 {main}