фільтрувати елементи у словнику python, де ключі містять певний рядок


97

Я кодер C, що розробляє щось на python. Я знаю, як зробити наступне на C (і, отже, на C-подібну логіку, застосовану до python), але мені цікаво, що це за спосіб 'Python'.

У мене є словник d, і я хотів би оперувати підмножиною елементів, лише той, хто має ключ (рядок), містить певний підрядок.

тобто логікою C буде:

for key in d:
    if filter_string in key:
        # do something
    else
        # do nothing, continue

Я гадаю, версія python буде щось на зразок

filtered_dict = crazy_python_syntax(d, substring)
for key,value in filtered_dict.iteritems():
    # do something

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

Мій словник не вкладений, і я використовую python 2.7



Відповіді:


188

Як щодо розуміння дикту :

filtered_dict = {k:v for k,v in d.iteritems() if filter_string in k}

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

Цей синтаксис вимагає Python 2.7 або новішої версії.

У Python 3 є лише dict.items(), не iteritems()так, щоб ви використовували:

filtered_dict = {k:v for (k,v) in d.items() if filter_string in k}

1
Чому ні filtered_dict = {k:d[k] for k in d if filter_string in k}?
четверте око

5
@thefourtheye Я збираюся здогадуватися, що мій швидший, оскільки це не вимагає d[k]пошуку.
Джонатан Рейнхарт,

Крім того, він говорить # do somethingу коментарях, але ми опускаємо сюди кілька ключів.
четверте око

Чи є iteritemsу нас Python 3? Я не думаю. Отже, моя версія буде сумісною, ні?
четверте око

1
У Python 3 ви б замінили iteritemsна items, що збігається з Python 2.7 iteritems.
Джонатан Рейнхарт,

18

Виберіть все, що є найбільш читабельним і легко обслуговуваним. Те, що ви можете виписати це в один рядок, не означає, що ви повинні. Ваше існуюче рішення наближається до того, що я б використовував, крім як користувальницькі iteritems, щоб пропустити пошук значень, і я ненавиджу вкладені ifs, якщо я можу їх уникнути:

for key, val in d.iteritems():
    if filter_string not in key:
        continue
    # do something

Однак якщо ви дійсно хочете, щоб щось дозволило вам пройти ітерацію через відфільтрований дикт, тоді я б не виконував двоетапний процес побудови відфільтрованого дикту та подальшої ітерації через нього, а замість цього використовував би генератор, бо що є більш пітонічним (і приголомшливим), ніж генератор?

Спочатку ми створюємо наш генератор, і хороший дизайн диктує, що ми робимо його досить абстрактним, щоб бути багаторазовим:

# The implementation of my generator may look vaguely familiar, no?
def filter_dict(d, filter_string):
    for key, val in d.iteritems():
        if filter_string not in key:
            continue
        yield key, val

І тоді ми можемо використовувати генератор, щоб вирішити вашу проблему приємно і чисто за допомогою простого, зрозумілого коду:

for key, val in filter_dict(d, some_string):
    # do something

Коротше кажучи: генератори чудові.


11

Ви можете використовувати вбудовану функцію фільтра для фільтрування словників, списків тощо на основі конкретних умов.

filtered_dict = dict(filter(lambda item: filter_str in item[0], d.items()))

Перевага полягає в тому, що ви можете використовувати його для різних структур даних.


Зверніть увагу, що це items:має бути item:у лямбда-визначенні.
bkribbs

Дякую @bkribbs за вказівку на помилку. Зараз я це виправив.
Pulkit

8
input = {"A":"a", "B":"b", "C":"c"}
output = {k:v for (k,v) in input.items() if key_satifies_condition(k)}

3
Мій метод використання iteritems()буде більш ефективним, ніж items().
Джонатан Рейнхарт,

@Jonathin Reinhart Я не знав про це. Дякую.
jspurim

2
Тільки на Python 2.7. У Python 3 існує лише такий items() , який діє як Python 2.7 iteritems.
Джонатан Рейнхарт

1
Питання явно стосується python 2.7
Brendan F

7

Джонатан запропонував вам підхід, використовуючи розуміння дикту у своїй відповіді . Ось такий підхід стосується того, як щось зробити .

Якщо ви хочете щось зробити зі значеннями словника, вам зовсім не потрібно розуміння словника:

Я використовую iteritems(), оскільки ви позначили своє запитання тегом

results = map(some_function, [(k,v) for k,v in a_dict.iteritems() if 'foo' in k])

Тепер результат буде у списку, що some_functionзастосовується до кожної пари ключ / значення словника, що є fooв його ключі.

Якщо ви просто хочете мати справу зі значеннями та ігнорувати ключі, просто змініть розуміння списку:

results = map(some_function, [v for k,v in a_dict.iteritems() if 'foo' in k])

some_function може бути будь-яким викликаним, тому лямбда також буде працювати:

results = map(lambda x: x*2, [v for k,v in a_dict.iteritems() if 'foo' in k])

Внутрішній список насправді не потрібен, оскільки ви також можете передати вираз генератора на карту:

>>> map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))
[4]

цікаво. як буде визначена функція some_? у першому випадку (k, v), це просто приймає два параметри? перший ключ, потім значення?
пам’ятка

Так, просто викликається. Отже map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))- це вам дасть [4].
Бурхан Халід

Це правильно, але більш пітонічним, ніж використання, mapє розуміння списку. [f(v) for k, v in d.iteritems() if substring in k]Я думаю, що це набагато читабельніше та ефективніше.
Davidmh

@memo Це не займе двох параметрів, а єдиний параметр із двома елементами. Існує також starmap, який розпакує два аргументи, однак це ледачий ітератор (його слід повторити перед виконанням, тобто results = list(starmap(...))або for result in starmap(...): ...).
nmclean
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.