V̓©rifier la pr̓©sence de plusieurs cl̓©s dans un dictionnaire

8 réponses
Avatar
Olivier Miakinen
Bonjour,

Le test « key in d » vérifie la présence d'une clé dans un dictionnaire.

Soit maintenant une liste de plusieurs clés keys=[key1, key2, ... keyn].

Quelle serait la façon la plus économique pour vérifier que toutes les clés
listées dans keys sont présentes dans le dictionnaire d ? Je n'ai pas besoin
de savoir précisément quelles clés sont présentes, je voudrais juste un test
qui me réponde True si tous les élements de keys sont bien des clés dans d,
False si au moins un élément de keys n'est pas une clé dans d.

Cordialement,
--
Olivier Miakinen

8 réponses

Avatar
Olivier Miakinen
Je réponds Í  moi-même, je crois que j'ai trouvé...
Le 25/04/2022 23:56, Olivier Miakinen a écrit :
Le test « key in d » vérifie la présence d'une clé dans un dictionnaire.
Soit maintenant une liste de plusieurs clés keys=[key1, key2, ... keyn].
Quelle serait la façon la plus économique pour vérifier que toutes les clés
listées dans keys sont présentes dans le dictionnaire d ? Je n'ai pas besoin
de savoir précisément quelles clés sont présentes, je voudrais juste un test
qui me réponde True si tous les élements de keys sont bien des clés dans d,
False si au moins un élément de keys n'est pas une clé dans d.

Ceci devrait faire l'affaire :
not (keys - d.keys())
Sauf erreur de ma part, ça répondra True si la différence entre mon tableau
de clés et les vraies clés du dictionnaire est un ensemble vide, False si
la différence contient au moins une clé (présente donc dans keys et pas dans
d.keys())
--
Olivier Miakinen
Avatar
Nicolas
Le 26/04/2022 Í  00:06, Olivier Miakinen a écrit :
Je réponds Í  moi-même, je crois que j'ai trouvé...
Le 25/04/2022 23:56, Olivier Miakinen a écrit :
Le test « key in d » vérifie la présence d'une clé dans un dictionnaire.
Soit maintenant une liste de plusieurs clés keys=[key1, key2, ... keyn].
Quelle serait la façon la plus économique pour vérifier que toutes les clés
listées dans keys sont présentes dans le dictionnaire d ? Je n'ai pas besoin
de savoir précisément quelles clés sont présentes, je voudrais juste un test
qui me réponde True si tous les élements de keys sont bien des clés dans d,
False si au moins un élément de keys n'est pas une clé dans d.

Ceci devrait faire l'affaire :
not (keys - d.keys())
Sauf erreur de ma part, ça répondra True si la différence entre mon tableau
de clés et les vraies clés du dictionnaire est un ensemble vide, False si
la différence contient au moins une clé (présente donc dans keys et pas dans
d.keys())

Jolie solution. Je n'y aurait pas pensé.
+1
Avatar
Alain Ketterlin
Olivier Miakinen <om+ writes:
Soit maintenant une liste de plusieurs clés keys=[key1, key2, ... keyn].
Quelle serait la façon la plus économique pour vérifier que toutes les clés
listées dans keys sont présentes dans le dictionnaire d ? Je n'ai pas besoin
de savoir précisément quelles clés sont présentes, je voudrais juste un test
qui me réponde True si tous les élements de keys sont bien des clés dans d,
False si au moins un élément de keys n'est pas une clé dans d.

Ceci devrait faire l'affaire :
not (keys - d.keys())
Sauf erreur de ma part, ça répondra True si la différence entre mon tableau
de clés et les vraies clés du dictionnaire est un ensemble vide, False si
la différence contient au moins une clé (présente donc dans keys et pas dans
d.keys())

Je m'interroge sur cette façon de faire... keys est une liste, d.keys()
une "vue", je ne vois pas bien de quel opérateur - il s'agit (le
résultat est un set, en tout cas). Bref, cela fait bien ce que tu veux
apparemment.
Si le dictionnaire est volumineux et la liste courte, il vaut peut-être
mieux faire quelque chose comme
all (k in d for k in keys)
cela évitera la création explicite d'un set dans le seul but de tester
s'il est vide, et j'imagine que cela arrête la recherche au premier
False, ce que semble confirmer la doc de all.
(Difficile d'en dire plus parce que toutes ces opérations ont une
complexité --voire un comportement -- mal documentée.)
-- Alain.
Avatar
Olivier Miakinen
Le 26/04/2022 Í  11:55, Alain Ketterlin a écrit :
not (keys - d.keys())

Je m'interroge sur cette façon de faire... keys est une liste, d.keys()
une "vue", je ne vois pas bien de quel opérateur - il s'agit (le
résultat est un set, en tout cas). Bref, cela fait bien ce que tu veux
apparemment.

Tu as raison de t'interroger, parce que j'ai lu trop vite la doc :
<https://docs.python.org/fr/3.8/library/stdtypes.html#frozenset.difference>
§
Remarque : Les méthodes union(), intersection(), difference(), et
symmetric_difference(), issubset(), et issuperset() acceptent n'importe
quel itérable comme argument, contrairement aux opérateurs équivalents
qui n'acceptent que des sets.
§
En lisant cette remarque, j'ai cru Í  tort qu'elle disait que « - »
acceptait n'importe quel itérable Í  gauche et Í  droite, alors qu'au
ça ne vaut pour « difference() », et encore, seulement pour son
argument !
Bon, il se trouve que ça marche (et que ça retourne effectivement un
set) mais je suis bien sͻr preneur d'une meilleure solution.
Si le dictionnaire est volumineux et la liste courte, il vaut peut-être
mieux faire quelque chose comme
all (k in d for k in keys)
cela évitera la création explicite d'un set dans le seul but de tester
s'il est vide, et j'imagine que cela arrête la recherche au premier
False, ce que semble confirmer la doc de all.

Ni le dictionnaire ni la liste ne sont très gros (moins de 20 entrées
pour le dictionnaire, et entre 3 et 6 pour la liste), mais ta solution
présente surtout l'intérêt d'être plus lisible. Cette commande 'all'
est vraiment ce qu'il me faut !
Bon, ayant créé un set, j'en avais profité pour l'afficher dans les
traces (« les données manquantes sont... »), mais je n'en ai pas plus
besoin que ça. Merci de m'avoir répondu, je pense que je vais vraiment
retenir ta proposition. J'essaye ça dès que possible.
(Difficile d'en dire plus parce que toutes ces opérations ont une
complexité --voire un comportement -- mal documentée.)

Certes. D'o͹ la nécessité de n'utiliser que ce qui est documenté, parce
que le reste risquerait de changer de comportement d'une version Í  une
autre.
--
Olivier Miakinen
Avatar
Alain Ketterlin
Olivier Miakinen <om+ writes:
not (keys - d.keys())


<https://docs.python.org/fr/3.8/library/stdtypes.html#frozenset.difference>
§
Remarque : Les méthodes union(), intersection(), difference(), et
symmetric_difference(), issubset(), et issuperset() acceptent n'importe
quel itérable comme argument, contrairement aux opérateurs équivalents
qui n'acceptent que des sets.
§
En lisant cette remarque, j'ai cru Í  tort qu'elle disait que « - »
acceptait n'importe quel itérable Í  gauche et Í  droite, alors qu'au
ça ne vaut pour « difference() », et encore, seulement pour son
argument !
Bon, il se trouve que ça marche (et que ça retourne effectivement un
set) mais je suis bien sͻr preneur d'une meilleure solution.

Oui, mais je pense que ça marche grÍ¢ce aux "reflected/swapped operands",
qui fait si il n'y a pas de __sub__ sur list, la méthode __rsub__ de set
(ou ce que renvoie keys()) est utilisée...
https://docs.python.org/3/reference/datamodel.html#object.__radd__
all (k in d for k in keys)

Ni le dictionnaire ni la liste ne sont très gros (moins de 20 entrées
pour le dictionnaire, et entre 3 et 6 pour la liste), mais ta solution
présente surtout l'intérêt d'être plus lisible. Cette commande 'all'
est vraiment ce qu'il me faut !

Dans ce cas, "beauty is in the eye of the beholder", choisis ce que tu
comprendras le plus facilement quand tu reliras le code dans 3
semaines/mois/années/.
-- Alain.
Avatar
Olivier Miakinen
Le 26/04/2022 19:37, Alain Ketterlin a écrit :
Oui, mais je pense que ça marche grÍ¢ce aux "reflected/swapped operands",
qui fait si il n'y a pas de __sub__ sur list, la méthode __rsub__ de set
(ou ce que renvoie keys()) est utilisée...
https://docs.python.org/3/reference/datamodel.html#object.__radd__

Eh bien je suis vraiment content d'avoir posé la question, parce que
j'étais loin d'imaginer tous ces mécanismes et que je trouve ça
passionnant !
Par ailleurs tu as raison, le __sub__ n'existe pas pour une liste alors que
le __rsub__ fonctionne sur un dict_keys :
% keys.__sub__(d.keys())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__sub__'
% d.keys().__rsub__(keys)
set()
% d.keys()
dict_keys([ ... ])
all (k in d for k in keys)


[...] ta solution
présente surtout l'intérêt d'être plus lisible. [...]

Dans ce cas, "beauty is in the eye of the beholder", choisis ce que tu
comprendras le plus facilement quand tu reliras le code dans 3
semaines/mois/années/.

Oui, mais il faut aussi penser que ce soit quelqu'un d'autre que moi
qui puisse devoir relire le code plus tard, alors j'ajouterai aussi un
commentaire disant en substance « on vérifie que toutes les clés requises
sont dans le dictionnaire ».
--
Olivier Miakinen
Avatar
Olivier Miakinen
Le 26/04/2022 11:55, Alain Ketterlin a écrit :
all (k in d for k in keys)

En cherchant la doc pour all() j'ai également trouvé any() qui va
m'être tout aussi utile :
<https://docs.python.org/fr/3/library/functions.html?highlight=any#all>
Encore merci !
--
Olivier Miakinen
Avatar
Olivier Miakinen
Le 26/04/2022 00:06, je me répondais :
Soit maintenant une liste de plusieurs clés keys=[key1, key2, ... keyn].


En réalité je ne sais pas pourquoi j'en ai fait une liste, c'est réellement
un ensemble qui correspond le mieux au besoin : keys={key1, key2, ... keyn}
Quelle serait la façon la plus économique pour vérifier que toutes les clés
listées dans keys sont présentes dans le dictionnaire d ? Je n'ai pas besoin
de savoir précisément quelles clés sont présentes, je voudrais juste un test
qui me réponde True si tous les élements de keys sont bien des clés dans d,
False si au moins un élément de keys n'est pas une clé dans d.

Ceci devrait faire l'affaire :
not (keys - d.keys())

Alain Ketterlin m'a donné une solution élégante avec all() mais je viens
d'en trouver une autre, Í  condition d'avoir un ensemble de clés plutÍ´t
qu'une liste, et sans créer de nouveau set :
keys.issubset(d.keys())
D'ailleurs j'utilisais déjÍ  une construction similaire ailleurs dans le
code, quand je voulais vérifier au contraire que toutes les clés du
dictionnaire font partie de celles autorisées dans l'ensemble keys :
keys.issuperset(d.keys())
<https://docs.python.org/fr/3/library/stdtypes.html?highlight=issuperset#frozenset.issubset>
Quant Í  ce que j'avais résolu avec un any(), il pourrait aussi se faire
avec un « not isdisjoint() ».
--
Olivier Miakinen