Come posso usare if / else nella comprensione di un dizionario?


138

Esiste un modo in Python 2.7+ per creare qualcosa di simile al seguente?

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

So che puoi fare qualsiasi cosa solo con 'if':

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

4
come raccontato da @Marcin, a dictè fatto di key:valueelementi, non stai costruendo un dictqui ma un set(vedi set letterali ).
medio

Risposte:


247

Hai già capito: A if test else Bè un'espressione valida di Python. L'unico problema con la comprensione del dict come mostrato è che il posto per un'espressione in una comprensione del dict deve avere due espressioni, separate da due punti:

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

La ifclausola finale funge da filtro, che è diverso dall'espressione condizionale.


28
Vale la pena ricordare che non è necessario disporre di una condizione if-else sia per la chiave che per il valore. Ad esempio, {(a if condition else b): value for key, value in dict.items()}funzionerà.
Jeremy Weirich,

5
@JeremyWeirich Non hai bisogno di avere un if-else per nessuno dei due se non vuoi.
Marcin,

@Marcin È possibile utilizzare solo "if" per la parte chiave e utilizzare "if" e "else" per la parte del valore?
nithin11,

14

@ Risposta di Marcin copre tutto, ma nel caso qualcuno volesse vedere un esempio reale, ne aggiungo due di seguito:

Supponiamo che tu abbia il seguente dizionario di set

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

e vuoi creare un nuovo dizionario le cui chiavi indicano se la stringa 'a'è contenuta nei valori oppure no, puoi usarla

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()}

quale cede

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

Supponiamo che tu abbia due dizionari come questo

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'}}

e vuoi sostituire le chiavi d1con quelle di d2se i rispettivi valori sono identici, potresti farlo

# 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()}

che dà

{'bad_key2': {'bar', 'foo'},
 'bad_key3': {'sad', 'so'},
 'good_key2': {'a', 'b', 'c'}}

per il tuo secondo esempio usando d1, d2ottengoAttributeError: 'dict_values' object has no attribute 'index'
alancalvitti il

@alancalvitti: grazie per averlo segnalato! La soluzione era per Python 2 e non funziona per Python 3; Ho aggiunto anche una soluzione Python 3.
Cleb

3

Nel caso in cui tu abbia condizioni diverse per valutare chiavi e valori, la risposta di @ Marcin è la strada da percorrere.

Se hai le stesse condizioni per chiavi e valori, stai meglio con le coppie di costruzione (chiave, valore) in un'espressione di generatore che si alimenta in dict():

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

È più facile da leggere e la condizione viene valutata solo una volta per chiave, valore.

Esempio con il prestito di dizionari del set di Cleb:

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

Supponiamo di voler aggiungere il suffisso solo keyscon ain its valuee valuein tal caso si desidera sostituirlo con la lunghezza dell'insieme. Altrimenti, la coppia chiave-valore dovrebbe rimanere invariata.

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'}}

0

Un altro esempio nell'uso di if / else nella comprensione del dizionario

Sto lavorando su un'applicazione desktop di immissione dati per il mio lavoro d'ufficio ed è comune per tale applicazione di immissione dati ottenere tutte le voci dal widget di input e scaricarle in un dizionario per ulteriori elaborazioni come la convalida o la modifica che dobbiamo restituire dati selezionati dal file ai widget di accesso, ecc.

Il primo round con la codifica tradizionale (8 righe):

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}}”

Secondo round ho provato a usare la comprensione del dizionario ma il loop è ancora lì (6 righe):

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}}”

Infine, con un'istruzione di comprensione del dizionario di una riga (1 riga):

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}}”

Uso python 3.8.3

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.