web-dev-qa-db-fra.com

Mappage sur des valeurs dans un dictionnaire python

Avec un dictionnaire { k1: v1, k2: v2 ... } Je veux obtenir { k1: f(v1), k2: f(v2) ... } à condition de passer une fonction f.

Existe-t-il une telle fonction intégrée? Ou dois-je faire

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

Idéalement j'écrirais simplement

my_dictionary.map_values(f)

ou

my_dictionary.mutate_values_with(f)

Autrement dit, peu importe si le dictionnaire original est muté ou si une copie est créée.

177
Tarrasch

Il n'y a pas une telle fonction; Pour ce faire, le plus simple consiste à utiliser une compréhension dictée:

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

En python 2.7, utilisez la méthode .iteritems() au lieu de .items() pour économiser de la mémoire. La syntaxe de compréhension de dict n'a pas été introduite jusqu'à python 2.7.

Notez qu’il n’existe pas non plus une telle méthode sur les listes; vous devez utiliser une liste de compréhension ou la fonction map().

En tant que tel, vous pouvez également utiliser la fonction map() pour traiter votre dict:

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

mais ce n'est pas si lisible, vraiment.

262
Martijn Pieters

Ces outils sont parfaits pour ce type de logique simple mais répétitive.

http://toolz.readthedocs.org/fr/latest/api.html#toolz.dicttoolz.valmap

Vous êtes là où vous voulez être.

import toolz
def f(x):
  return x+1

toolz.valmap(f, my_list)
21
Jesse Smith

Vous pouvez le faire sur place plutôt que de créer un nouveau dict, ce qui peut être préférable pour les grands dictionnaires (si vous n'avez pas besoin de copie).

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)

résultats dans my_dictionary contenant:

{'a': 2, 'b': 3}
19
gens

Alors que ma réponse initiale manquait le but (en essayant de résoudre ce problème avec la solution de Accès à la clé dans l’usine de defaultdict ), je l’ai retravaillée pour proposer une solution réelle à la question actuelle.

C'est ici:

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

Usage:

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

L'idée est de sous-classer le dict d'origine pour lui donner la fonctionnalité désirée: "mapper" une fonction sur toutes les valeurs.

Le point positif est que ce dictionnaire peut être utilisé pour stocker les données d'origine comme s'il s'agissait d'une dict, tout en transformant les données sur demande avec un rappel.

Bien sûr, n'hésitez pas à nommer la classe et la fonction comme vous le souhaitez (le nom choisi dans cette réponse est inspiré de la fonction array_walk() de PHP).

Note: Ni le bloc try-except ni les instructions return ne sont obligatoires pour la fonctionnalité, elles sont là pour imiter davantage le comportement du array_walk de PHP.

4
7heo.tk

En raison de PEP-0469 qui a renommé iteritems () en items () et PEP-3113 qui a supprimé le paramètre Tuple décompressant, dans Python 3.x, vous devez écrire Martijn Pieters ♦ answer comme ça:

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

Je viens de traverser ce cas d'utilisation. J'ai implémenté réponse de gens , en ajoutant une approche récursive pour gérer les valeurs qui sont également dictées:

def mutate_dict_in_place(f, d):
    for k, v in d.iteritems():
        if isinstance(v, dict):
            mutate_dict_in_place(f, v)
        else:
            d[k] = f(v)

# Exemple handy usage
def utf8_everywhere(d):
    mutate_dict_in_place((
        lambda value:
            value.decode('utf-8')
            if isinstance(value, bytes)
            else value
        ),
        d
    )

my_dict = {'a': b'byte1', 'b': {'c': b'byte2', 'd': b'byte3'}}
utf8_everywhere(my_dict)
print(my_dict)

Cela peut être utile lorsqu'il s'agit de fichiers json ou yaml qui encodent des chaînes sous forme d'octets dans Python 2.

0
Oyono

Pour éviter d’indexer depuis lambda, par exemple:

rval = dict(map(lambda kv : (kv[0], ' '.join(kv[1])), rval.iteritems()))

Vous pouvez aussi faire:

rval = dict(map(lambda(k,v) : (k, ' '.join(v)), rval.iteritems()))
0
yourstruly