2011-01-15 9 views
6

Tengo diccionario comoexpresión de una línea de mapa diccionario a otro

d = {'user_id':1, 'user':'user1', 'group_id':3, 'group_name':'ordinary users'} 

y el diccionario de "mapeo" como:

m = {'user_id':'uid', 'group_id':'gid', 'group_name':'group'} 

Todo lo que quiero llaves en primer diccionario con claves de "reemplazar" desde el segundo (por ejemplo, reemplazar 'user_id' por 'uid', etc.) Sé que las claves son inmutables y sé cómo hacerlo con la instrucción 'if/else'.

¿Pero tal vez hay una forma de hacerlo en una expresión de línea?

+0

¿Qué versión de python? – orlp

+0

uid es el valor en el segundo, no la clave. ¿Cuál es el resultado que te gusta ver? –

Respuesta

15

Claro:

d = dict((m.get(k, k), v) for (k, v) in d.items()) 
+2

debe ser 'for (k, v) en d.items' not' for (k, v) en d' – mouad

+0

Fixed. (Y re-fixed; 'items' necesita ser llamado como un método.) –

+1

Para Python 2.x, sería más apropiado usar' d.iteritems() 'en lugar de' d.items() '(pero esto solo importa para los diccionarios realmente grandes). –

5

En 3.x:

d = {m.get(key, key):value for key, value in d.items()} 

Funciona mediante la creación de un nuevo diccionario que contiene todos los valores de d y se asigna a una nueva clave. La clave se recupera así: m[key] if m in key else key, pero luego con la función .get predeterminada (que admite valores predeterminados si la clave no existe).

+0

+1 para agregar la versión idiomática v3. –

0

¿Por qué querrías hacerlo en una línea?

result = {} 
for k, v in d.iteritems(): 
    result[m.get(k, k)] = v 
+2

Porque la lista/dict/cualquier comprensión es muy pythonesque y rapidísimo? – orlp

+0

"Pythonic", pero sí, más o menos eso. –

13

Tomemos el código excelente desde @karlknechtel y ver lo que hace:

>>> d = dict((m.get(k, k), v) for (k, v) in d.items()) 
{'gid': 3, 'group': 'ordinary users', 'uid': 1, 'user': 'user1'} 

Pero ¿cómo funciona?

Para construir un diccionario, puede usar la función dict(). Espera una lista de tuplas. En 3.x y> 2.7, también puede usar la comprensión del diccionario (ver respuesta por @nightcracker).

Analicemos el argumento de dict. Al principio, necesitamos una lista de todos los artículos en m. Cada elemento es una tupla en el formato (clave, valor).

>>> d.items() 
[('group_id', 3), ('user_id', 1), ('user', 'user1'), ('group_name', 'ordinary users')] 

dado un valor de clave k, podríamos obtener el valor de la clave derecha desde m haciendo m[k].

>>> k = 'user_id' 
>>> m[k] 
'uid' 

Desafortunadamente, no existen también todas las claves en d en m.

>>> k = 'user' 
>>> m[k] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
KeyError: 'user' 

Para evitar eso, se puede utilizar d.get(x, y), que devuelve d[x] si la clave x existe, o el valor predeterminado y si no lo hace. Ahora, si una clave k de d no existe en m, solo la guardamos, por lo que el valor predeterminado es k.

>>> m.get(k, k). 
'user' 

Ahora estamos listos para construir una lista de tuplas para abastecer a dict(). Para construir una lista en una línea, podemos usar list comprehension.

Para construir una lista de casillas, que iba a escribir esto:

>>> [x**2 for x in range(5)] 
[0, 1, 4, 9, 16] 

En nuestro caso, se ve así:

>>> [(m.get(k, k), v) for (k, v) in d.items()] 
[('gid', 3), ('uid', 1), ('user', 'user1'), ('group', 'ordinary users')] 

que es un bocado, vamos a ver que de nuevo.

Dame una lista [...], que consta de tuplas:

[(.., ..) ...] 

Quiero una tupla para cada artículo x en d:

[(.., ..) for x in d.items()] 

sabemos que cada elemento es una tupla con dos componentes, por lo que podemos expandirlo a dos variables k y v.

[(.., ..) for (k, v) in d.items()] 

Cada tupla debería tener la tecla derecha desde m como primer componente, o k si k no existe en m, y el valor de d.

[(m.get(k, k), v) for (k, v) in d.items()] 

Podemos pasarlo como argumento al dict().

>>> dict([(m.get(k, k), v) for (k, v) in d.items()]) 
{'gid': 3, 'group': 'ordinary users', 'uid': 1, 'user': 'user1'} 

Se ve bien! Pero espera, podrías decir, @karlknechtel no usó corchetes.

Derecho, él no usó una lista de comprensión, sino un generator expression. Simplemente hablando, la diferencia es que una lista de comprensión construye toda la lista en la memoria, mientras que la expresión de un generador se calcula en un elemento a la vez. Si una lista sirve como un resultado intermedio, generalmente es una buena idea usar una expresión generadora. En este ejemplo, realmente no hace una diferencia, pero es un buen hábito de acostumbrarse.

Las expresiones generadoras equivalentes se ve así:

>>> ((m.get(k, k), v) for (k, v) in d.items()) 
<generator object <genexpr> at 0x1004b61e0> 

Si pasa un generador de expresión como argumento de una función, por lo general puede omitir los paréntesis exteriores. Por último, se obtiene:

>>> dict((m.get(k, k), v) for (k, v) in d.items()) 
{'gid': 3, 'group': 'ordinary users', 'uid': 1, 'user': 'user1'} 

No pasa a ser determinante en una línea de código. Algunos dicen que esto es ilegible, pero una vez que estás acostumbrado, extender este código en varias líneas parece ilegible. Simplemente no exageres. La comprensión de listas y las expresiones generadoras son muy poderosas, pero con gran poder viene una gran responsabilidad. +1 para una buena pregunta!

Cuestiones relacionadas