Картографування значень у словнику python


243

Давши словник, який { k1: v1, k2: v2 ... }я хочу отримати за { k1: f(v1), k2: f(v2) ... }умови, що я передаю функцію f.

Чи є така вбудована функція? Або я повинен робити

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

В ідеалі я просто писав би

my_dictionary.map_values(f)

або

my_dictionary.mutate_values_with(f)

Тобто, для мене це не має значення, якщо оригінальний словник мутовано чи створена його копія.


2
Кращим способом написання вашого прикладу був би dict((k, f(v)) for k, v in mydict.iteritems()), тобто без квадратних дужок, який би перешкоджав створенню проміжного списку через генератор.
bereal

Відповіді:


354

Немає такої функції; найпростіший спосіб зробити це - розуміння дикту:

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

У python 2.7 використовуйте .iteritems()метод замість того, .items()щоб зберегти пам'ять. Синтаксис розуміння дикту не був введений до python 2.7.

Зауважте, що такого методу в списках також немає; вам доведеться скористатися розумінням списку або map()функцією.

Ви можете використовувати цю map()функцію і для обробки свого dict:

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

але насправді це не так читабельно.


5
+1: це я теж би зробив. dict(zip(a, map(f, a.values())))дещо коротше, але я повинен думати про те, що це робить, і нагадати собі, що так, ключі та значення повторюються в тому ж порядку, якщо диктант не зміниться. Мені зовсім не треба думати про те, що робить dictcomp, і тому це правильна відповідь.
DSM

2
@chiborg: це тому, що замість того, щоб шукати всі пари ключових значень за один раз, тепер ви використовуєте кількість my_dictionary.__getitem__викликів разів .
Martijn Pieters

1
Зауважте, що оскільки PEP3113 (реалізований у python 3.x) параметри кортежу більше не підтримуються: lambda (k,v): (k, f(v))його слід переписати на щось на зразокlambda k_v: (k_v[0], f(k_v[1]))
normanius

1
Чому при розпакуванні параметра вилучено? Як це поліпшення ?
javadba

3
виходячи з мови FP, Python здасться неймовірно незграбним.
juanchito


21

Ви можете зробити це на місці, а не створити новий дикт, який може бути кращим для великих словників (якщо копія вам не потрібна).

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)

результати my_dictionaryмістять:

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

1
Круто, ви повинні можливо перейменувати mapdictв mutate_values_withабо що - то , щоб зробити його кристально ясно , що ви переписати Dict! :)
Тарраш

2
zip(d.keys(), d.values())працює для інших версій замістьiteritems()
ytpillai

1
"zip" @ytpillai або розуміння роблять копію, а не зміну значень на місці, що є метою моєї відповіді. Прийнята відповідь найкраща, коли копія в порядку.
gens

1
Вибачте, я не здогадувався, що ви хочете використовувати метод предметів. Однак можливе ще одне вдосконалення до цього (для користувачів, які не користуються Python 2.7){k:f(v) for k,v in iter(d.items())}
ytpillai

1
Економлять місце, зробивши ітератор
ytpillai


4

Хоча моя оригінальна відповідь пропустила суть (намагаючись вирішити цю проблему за допомогою рішення ключа доступу до фабрики за замовчуванням ), я переробив її, щоб запропонувати фактичне рішення цього питання.

Ось:

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

Використання:

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

Ідея полягає в підкласі початкового диктату, щоб надати йому бажану функціональність: "відображення" функції над усіма значеннями.

Плюсом є те, що цей словник може використовуватися для зберігання вихідних даних так, як якщо б це було dict, одночасно перетворюючи будь-які дані за запитом із зворотним зв'язком.

Звичайно, не соромтесь називати клас та функцію так, як вам потрібно (ім'я, вибране в цій відповіді, натхнене функцією PHP array_walk()).

Примітка: Ні блок try-, exceptні returnоператори не є обов'язковими для функціональності, вони є там, щоб ще більше імітувати поведінку PHP array_walk.


1
Це не вдається вирішити питання щодо ОП, оскільки __missing__метод не буде викликаний для існуючих ключів, які ми хочемо перетворити, якщо тільки заводський метод якось не використовує диктант початків як резервний, але оскільки це не є частиною використання прикладу, Я вважаю це незадовільною відповіддю на існуючу проблему.
Каос

Які існуючі ключі?
7heo.tk

З OP: Given a dictionary { k1: v1, k2: v2 ... } .... Тобто, у вас вже є dictпочаток з ..
Каос

Я хотів би сказати, що ми обидва праві; але я вважаю, що ми обидва помиляємось. Ви праві, що моя відповідь не відповідає на запитання; але не з тієї причини, на яку ви посилалися. Я просто пропустив суть, даючи спосіб отримати {v1: f(v1), v2: f(v2), ...}дане [v1, v2, ...], а не дав дікта. Я відредагую свою відповідь, щоб виправити це.
7heo.tk

2

Щоб не проводити індексацію зсередини лямбда, виконайте такі дії:

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

Ви також можете зробити:

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

Це розумна маніпуляція в межах 2-кортежу у другому прикладі. Однак, він використовує автоматичне розпакування кортежу в лямбда, яке більше не підтримується в Python 3. Тому lambda(k,v)не буде працювати. Дивіться stackoverflow.com/questions/21892989/…
Джонатан Комар

0

Щойно зіткнувся з цим випадком використання. Я реалізував відповідь gens , додавши рекурсивний підхід до обробки значень, які також є диктом:

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)

Це може бути корисно при роботі з файлами json або yaml, які кодують рядки як байти в Python 2

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.