2012-09-01 14 views
142

Gegeben ein Wörterbuch { k1: v1, k2: v2 ... } Ich möchte { k1: f(v1), k2: f(v2) ... } erhalten, vorausgesetzt ich übergebe eine Funktion f.Mapping über Werte in einem Python-Wörterbuch

Gibt es eine solche eingebaute Funktion? Oder muss ich

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

zu tun haben Idealerweise würde ich schreiben gerade

my_dictionary.map_values(f) 

oder

my_dictionary.mutate_values_with(f) 

Das heißt, spielt es keine Rolle für mich, wenn das ursprüngliche Wörterbuch mutiert oder Eine Kopie wird erstellt.

+2

Eine bessere Art und Weise des Schreibens Ihr Beispiel wäre 'dict ((k, f (v)) für k, v in mydict.iteritems())', dh ohne die eckigen Klammern, die die Erstellung einer Zwischenliste über einen Generator verhindern würden. – bereal

Antwort

205

Es gibt keine solche Funktion; der einfachste Weg, dies zu tun, ist ein dict Verständnis zu verwenden:

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

In Python 2.7, verwenden Sie die .iteritems() Methode anstelle von .items() Speicher zu speichern. Die Diktat-Verständnis-Syntax wurde erst mit Python 2.7 eingeführt.

Beachten Sie, dass es keine solche Methode für Listen gibt; Sie müssten ein Listenverständnis oder die map() Funktion verwenden.

Als solche könnten Sie die map() Funktion für die Bearbeitung Ihrer dict verwenden auch:

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

aber das ist nicht so lesbar, wirklich.

+4

+1: das würde ich auch tun. 'dict (zip (a, map (f, a.values ​​()))) ist geringfügig kürzer, aber ich muss darüber nachdenken, was es tut, und mich daran erinnern, dass ja, Schlüssel und Werte in der gleichen Reihenfolge durchlaufen werden wenn sich das Diktat nicht ändert. Ich muss gar nicht darüber nachdenken, was das Diktat macht, und so ist es die richtige Antwort. – DSM

+0

@DSM: Ja, der 'zip (adict, map (f, adict.values ​​())) Trick erfordert viel zu viel Verständnis von dem Code-Leser, nicht zu vergessen, eine ruhige Hand beim Hinzufügen aller schließenden Parameter! :-P –

+0

'{k: f (mein_dictionary [k]) für k in my_dictionary}' ist ein bisschen kürzer, aber interessanterweise ist es auch etwas langsamer (wenn man es mit 'timeit', einem 500-Items-Diktat und 'str()' für 'f'). Ich weiß nicht warum. – chiborg

13

Sie können dies an Ort und Stelle, anstatt eine neue dict zu schaffen, die für große Wörterbücher bevorzugt sein kann (wenn Sie nicht über eine Kopie benötigen).

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) 

Ergebnisse in my_dictionary enthalten:

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

Cool, du solltest vielleicht 'mapdict' in' mutate_values_with' umbenennen oder etwas, um klar zu machen, dass du das Diktat neu schreibst! :) – Tarrasch

+0

@Tarrash Vereinbarte; Ich habe die Funktion umbenannt. Vielen Dank. – gens

+1

'zip (d.keys(), d.values ​​())' funktioniert für mehr Versionen anstelle von 'iteritems()' – ytpillai

2

Während meiner ersten Antwort den Punkt verpasst (indem man versucht, dieses Problem mit der Lösung Accessing key in factory of defaultdict zu lösen), ich habe es überarbeitet eine tatsächliche Lösung vorzuschlagen, die vorliegende Frage.

Hier ist sie:

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

Verbrauch:

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

Die Idee ist, die ursprüngliche dict Unterklasse sie die gewünschte Funktionalität zu geben: "Mapping" eine Funktion über alle Werte.

Der Pluspunkt ist, dass dieses Wörterbuch verwendet werden kann, um die ursprünglichen Daten zu speichern, als ob es eine dict wäre, während alle Daten auf Anfrage mit einem Rückruf umgewandelt werden.

Natürlich können Sie die Klasse und die Funktion beliebig benennen (der in dieser Antwort gewählte Name ist von der PHP-Funktion array_walk() inspiriert).

Hinweis: Weder die try-except Block noch die return Aussagen für die Funktionalität obligatorisch sind, sind sie dort weiter das Verhalten des array_walk PHP zu imitieren.

+1

Dies löst die OP-Frage nicht, da die '__missing__'-Methode nicht für existierende Schlüssel aufgerufen wird, die wir transformieren wollen, es sei denn, die Factory-Methode verwendet den Ursprung dict als ein Fallback irgendwie, aber da dies nicht Teil des Beispiels Verwendung ist, halte ich dies für eine unbefriedigende Antwort auf das Problem zur Hand. – Kaos

+0

Welche vorhandenen Schlüssel? –

+0

Aus dem OP: 'Gegeben ein Wörterbuch {k1: v1, k2: v2 ...} ...'. Das heißt, Sie haben bereits ein "dict" zu beginnen. – Kaos

2

Aufgrund PEP-0469 der iteritems() umbenannt, um Elemente() und PEP-3113 die Tuple Parameter entfernt Auspacken in Python 3.x sollten Sie Martijn Pieters♦ answer wie folgt schreiben:

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