2012-09-01 17 views
142

Dado un diccionario { k1: v1, k2: v2 ... } Deseo obtener { k1: f(v1), k2: f(v2) ... } siempre que pase una función f.Mapeo de valores en un diccionario Python

¿Existe alguna función integrada? O tengo que hacer

dict([(k, f(v)) for (k, v) in my_dictionary.iteritems()]) 

Idealmente me acaba de escribir

my_dictionary.map_values(f) 

o

my_dictionary.mutate_values_with(f) 

Es decir, no me importa si el diccionario original se muta o se crea una copia

+2

Una mejor manera de escribir su ejemplo sería 'dict ((k, f (v)) para k, v en mydict.iteritems())', es decir, sin los corchetes, que impediría la creación de una lista intermedia a través de un generador. – bereal

Respuesta

205

No hay tal función; la forma más sencilla de hacerlo es utilizar una comprensión dict:

my_dictionary = {k: f(v) for k, v in my_dictionary.items()} 

en Python 2.7, utilice el método .iteritems() en lugar de .items() para ahorrar memoria. La sintaxis de comprensión dict no se introdujo hasta que Python 2.7.

Tenga en cuenta que tampoco existe dicho método en las listas; tendrías que usar una lista de comprensión o la función map().

Como tal, se puede utilizar la función map() para procesar su dict así:

my_dictionary = dict(map(lambda kv: (kv[0], f(kv[1])), my_dictionary.iteritems())) 

pero eso no es tan fácil de leer, en realidad.

+4

+1: esto es lo que yo también haría. 'dict (zip (a, map (f, a.values ​​())))' es marginalmente más corto, pero tengo que pensar en lo que está haciendo, y recordarme que sí, las claves y los valores se repiten en el mismo orden si el dict no cambia No tengo que pensar en absoluto sobre lo que está haciendo el dictcomp, así que es la respuesta correcta. – DSM

+0

@DSM: Sí, el truco 'zip (adict, map (f, adict.values ​​())))' requiere demasiada comprensión del lector ocasional de códigos, ¡sin mencionar una mano firme al agregar todos los params de cierre! :-P –

+0

'{k: f (my_dictionary [k]) para k en my_dictionary}' es un poco más corto, pero curiosamente también es un poco más lento (al sincronizarlo con 'timeit', un dict de 500 elementos y 'str()' para 'f'). No sé por qué. – chiborg

13

Usted puede hacer esto en contexto, en lugar de crear una nueva dict, que puede ser preferible para grandes diccionarios (si no necesita una copia).

def mutate_dict(f,d): 
    for k, v in d.iteritems(): 
     d[k] = f(v) 

my_dictionary = {'a':1, 'b':2} 
mutate_dict(lambda x: x+1, my_dictionary) 

resultados en my_dictionary que contienen:

{'a': 2, 'b': 3} 
+1

Genial, tal vez deberías renombrar 'mapdict' a' mutate_values_with' o algo así para que quede claro que reescribes el dict! :) – Tarrasch

+0

@Tarrash De acuerdo; Cambié el nombre de la función. Gracias. – gens

+1

'zip (d.keys(), d.values ​​())' funciona para más versiones en lugar de 'iteritems()' – ytpillai

2

Si bien mi respuesta original se perdió el punto (al tratar de resolver este problema con la solución a Accessing key in factory of defaultdict), que han vuelto a trabajar a proponer una solución real a la presente pregunta.

aquí está:

class walkableDict(dict): 
    def walk(self, callback): 
    try: 
     for key in self: 
     self[key] = callback(self[key]) 
    except TypeError: 
     return False 
    return True 

Uso:

>>> d = walkableDict({ k1: v1, k2: v2 ... }) 
>>> d.walk(f) 

La idea es subclase el dict originales para darle la funcionalidad deseada: "mapeo" una función sobre todos los valores.

El punto positivo es que este diccionario se puede utilizar para almacenar los datos originales como si fuera dict, mientras se transforman los datos bajo petición con una devolución de llamada.

Por supuesto, siéntase libre de nombrar la clase y la función de la manera que desee (el nombre elegido en esta respuesta está inspirado en la función array_walk() de PHP).

Nota: Ni el try - bloque except ni los return declaraciones son obligatorias para la funcionalidad, que están ahí para imitar aún más el comportamiento del PHP de array_walk.

+1

Esto no soluciona la pregunta OP ya que no se llamará al método' __missing__' para las claves existentes, que queremos transformar, a menos que el método de fábrica haya pasado use el origen dict como un retroceso de alguna manera, pero como eso no forma parte del uso del ejemplo, considero que esta es una respuesta insatisfactoria al problema en cuestión. – Kaos

+0

¿Qué teclas existentes? –

+0

Desde el OP: 'Dado un diccionario {k1: v1, k2: v2 ...} ...'. Es decir, ya tiene un 'dict' para empezar ... – Kaos

2

Debido a PEP-0469 la que renombró iteritems() a los elementos() y PEP-3113 que eliminó parámetro Tupla desembalaje, en Python 3.x debe escribir Martijn Pieters♦ answer así:

my_dictionary = dict(map(lambda item: (item[0], f(item[1]), my_dictionary.items())) 
Cuestiones relacionadas