¿Cómo puedo usar if / else en un diccionario de comprensión?


138

¿Existe alguna forma en Python 2.7+ para hacer algo como lo siguiente?

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

Sé que puedes hacer cualquier cosa con solo 'if':

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

44
según lo dicho por @Marcin, a dictestá hecho de key:valueelementos, no estás construyendo un dictaquí sino un set(ver conjunto de literales ).
mdeous

Respuestas:


247

Ya lo tienes: A if test else Bes una expresión válida de Python. El único problema con su comprensión dict como se muestra es que el lugar para una expresión en una comprensión dict debe tener dos expresiones, separadas por dos puntos:

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

La ifcláusula final actúa como un filtro, que es diferente de tener la expresión condicional.


28
Vale la pena mencionar que no necesita tener una condición if-else para la clave y el valor. Por ejemplo, {(a if condition else b): value for key, value in dict.items()}funcionará.
Jeremy Weirich

55
@JeremyWeirich No necesita tener un if-else para ninguno de ellos si no lo desea.
Marcin

@Marcin ¿Es posible para mí usar solo "if" para la parte clave y usar "if" y "else" para la parte del valor?
nithin11

14

La respuesta de @ Marcin lo cubre todo, pero en caso de que alguien quiera ver un ejemplo real, agrego dos a continuación:

Digamos que tienes el siguiente diccionario de conjuntos

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

y desea crear un nuevo diccionario cuyas claves indiquen si la cadena 'a'está contenida en los valores o no, puede usar

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

cuyos rendimientos

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

Ahora supongamos que tiene dos diccionarios como este

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

y desea reemplazar las claves d1por las claves de d2si sus valores respectivos son idénticos, podría hacer

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

lo que da

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

para su segundo ejemplo usando d1, d2obtengoAttributeError: 'dict_values' object has no attribute 'index'
alancalvitti

@alancalvitti: ¡gracias por señalar esto! La solución fue para Python 2 y no funciona para Python 3; También agregué una solución Python 3.
Cleb

3

En caso de que tenga diferentes condiciones para evaluar las claves y los valores, la respuesta de @ Marcin es el camino a seguir.

Si tiene la misma condición para las claves y los valores, es mejor construir tuplas (clave, valor) en una expresión de generador que alimenta dict():

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

Es más fácil de leer y la condición solo se evalúa una vez por clave, valor.

Ejemplo con tomar prestado el diccionario de conjuntos de @ Cleb:

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

Supongamos que desea sufijo solamente keyscon aen su valuey desea que la valuereemplazó con la longitud del conjunto en un caso así. De lo contrario, el par clave-valor debería permanecer sin cambios.

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

Otro ejemplo en el uso de if / else en la comprensión del diccionario

Estoy trabajando en la aplicación de escritorio de entrada de datos para mi propio trabajo de oficina, y es común que dicha aplicación de entrada de datos obtenga todas las entradas del widget de entrada y las descargue en un diccionario para su posterior procesamiento como validación o edición, que debemos devolver datos seleccionados desde el archivo de regreso a los widgets de entrada, etc.

La primera ronda con codificación tradicional (8 líneas):

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

En la segunda ronda, intenté usar la comprensión del diccionario, pero el bucle sigue ahí (6 líneas):

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

Finalmente, con una declaración de comprensión de diccionario de una línea (1 línea):

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

Yo uso python 3.8.3

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.