Comment puis-je utiliser if / else dans une compréhension de dictionnaire?

138

Existe-t-il un moyen dans Python 2.7+ de créer quelque chose comme ce qui suit?

{ something_if_true if condition else something_if_false for key, value in dict_.items() }

Je sais que vous pouvez faire n'importe quoi avec juste 'si':

{ something_if_true for key, value in dict_.items() if condition}
diegueus9
la source
4
comme le dit @Marcin, a dictest fait d' key:valueéléments, vous ne construisez pas un dictici mais un set(voir les littéraux définis ).
mdeous

Réponses:

247

Vous l'avez déjà: A if test else Best une expression Python valide. Le seul problème avec votre compréhension de dict, comme indiqué, est que la place d'une expression dans une compréhension de dict doit avoir deux expressions, séparées par deux points:

{ (some_key if condition else default_key):(something_if_true if condition
          else something_if_false) for key, value in dict_.items() }

La ifclause finale agit comme un filtre, ce qui est différent d'avoir l'expression conditionnelle.

Marcin
la source
28
Il convient de mentionner que vous n'avez pas besoin d'avoir une condition if-else pour la clé et la valeur. Par exemple, {(a if condition else b): value for key, value in dict.items()}fonctionnera.
Jeremy Weirich le
5
@JeremyWeirich Vous n'avez pas besoin d'avoir un if-else pour l'un ou l'autre si vous ne le souhaitez pas.
Marcin
@Marcin Est-il possible pour moi d'utiliser uniquement "if" pour la partie clé et d'utiliser à la fois "if" et "else" pour la partie valeur?
nithin11
14

La réponse de @ Marcin couvre tout, mais juste au cas où quelqu'un voudrait voir un exemple réel, j'ajoute deux ci-dessous:

Disons que vous avez le dictionnaire d'ensembles suivant

d = {'key1': {'a', 'b', 'c'}, 'key2': {'foo', 'bar'}, 'key3': {'so', 'sad'}}

et que vous souhaitez créer un nouveau dictionnaire dont les clés indiquent si la chaîne 'a'est contenue dans les valeurs ou non, vous pouvez utiliser

dout = {"a_in_values_of_{}".format(k) if 'a' in v else "a_not_in_values_of_{}".format(k): v for k, v in d.items()}

qui donne

{'a_in_values_of_key1': {'a', 'b', 'c'},
 'a_not_in_values_of_key2': {'bar', 'foo'},
 'a_not_in_values_of_key3': {'sad', 'so'}}

Supposons maintenant que vous ayez deux dictionnaires comme celui-ci

d1 = {'bad_key1': {'a', 'b', 'c'}, 'bad_key2': {'foo', 'bar'}, 'bad_key3': {'so', 'sad'}}
d2 = {'good_key1': {'foo', 'bar', 'xyz'}, 'good_key2': {'a', 'b', 'c'}}

et vous voulez remplacer les clés d1par les clés de d2si les valeurs respectives sont identiques, vous pouvez faire

# here we assume that the values in d2 are unique
# Python 2
dout2 = {d2.keys()[d2.values().index(v1)] if v1 in d2.values() else k1: v1 for k1, v1 in d1.items()}

# Python 3
dout2 = {list(d2.keys())[list(d2.values()).index(v1)] if v1 in d2.values() else k1: v1 for k1, v1 in d1.items()}

qui donne

{'bad_key2': {'bar', 'foo'},
 'bad_key3': {'sad', 'so'},
 'good_key2': {'a', 'b', 'c'}}
Cleb
la source
pour votre deuxième exemple en utilisant d1, d2je reçoisAttributeError: 'dict_values' object has no attribute 'index'
alancalvitti
@alancalvitti: merci de l'avoir signalé! La solution était pour Python 2 et ne fonctionne pas pour Python 3; J'ai également ajouté une solution Python 3.
Cleb
3

Si vous avez différentes conditions à évaluer pour les clés et les valeurs, la réponse de @ Marcin est la voie à suivre.

Si vous avez la même condition pour les clés et les valeurs, il vaut mieux créer des doubles (clé, valeur) dans une expression de générateur alimentant dict():

dict((modify_k(k), modify_v(v)) if condition else (k, v) for k, v in dct.items())

C'est plus facile à lire et la condition n'est évaluée qu'une seule fois par clé, valeur.

Exemple d'emprunt du dictionnaire d'ensembles de @ Cleb:

d = {'key1': {'a', 'b', 'c'}, 'key2': {'foo', 'bar'}, 'key3': {'so', 'sad'}}

Supposons que vous vouliez suffixer uniquement keysavec adans son valueet que vous vouliez le valueremplacer par la longueur de l'ensemble dans un tel cas. Sinon, la paire clé-valeur doit rester inchangée.

dict((f"{k}_a", len(v)) if "a" in v else (k, v) for k, v in d.items())
# {'key1_a': 3, 'key2': {'bar', 'foo'}, 'key3': {'sad', 'so'}}
Darkonaut
la source
0

Un autre exemple d'utilisation de if / else dans la compréhension du dictionnaire

Je travaille sur une application de bureau de saisie de données pour mon propre travail de bureau, et il est courant pour une telle application de saisie de données d'obtenir toutes les entrées du widget d'entrée et de les vider dans un dictionnaire pour un traitement ultérieur comme la validation ou l'édition que nous devons retourner les données sélectionnées du fichier aux widgets d'entrée, etc.

Le premier tour en utilisant le codage traditionnel (8 lignes):

entries = {'name': 'Material Name', 'maxt': 'Max Working Temperature', 'ther': {100: 1.1, 200: 1.2}}

a_dic, b_dic = {}, {}

for field, value in entries.items():
    if field == 'ther':
        for k,v in value.items():
            b_dic[k] = v
        a_dic[field] = b_dic
    else:
        a_dic[field] = value
    
print(a_dic)
 {'name': 'Material Name', 'maxt': 'Max Working Temperature', 'ther': {100: 1.1, 200: 1.2}}”

Au deuxième tour, j'ai essayé d'utiliser la compréhension du dictionnaire mais la boucle est toujours là (6 lignes):

entries = {'name': 'Material Name', 'maxt': 'Max Working Temperature', 'ther': {100: 1.1, 200: 1.2}}

for field, value in entries.items():
    if field == 'ther':
        b_dic = {k:v for k,v in value.items()}
        a_dic[field] = b_dic
    else:
        a_dic[field] = value
    
print(a_dic)
 {'name': 'Material Name', 'maxt': 'Max Working Temperature', 'ther': {100: 1.1, 200: 1.2}}”

Enfin, avec un énoncé de compréhension du dictionnaire sur une ligne (1 ligne):

entries = {'name': 'Material Name', 'maxt': 'Max Working Temperature', 'ther': {100: 1.1, 200: 1.2}}

a_dic = {field:{k:v for k,v in value.items()} if field == 'ther' 
        else value for field, value in entries.items()}
    
print(a_dic)
 {'name': 'Material Name', 'maxt': 'Max Working Temperature', 'ther': {100: 1.1, 200: 1.2}}”

J'utilise python 3.8.3

KokoEfraim
la source