фильтровать элементы в словаре 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

памятка
источник
stackoverflow.com/questions/2844516/python-filter-a-dictionary
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Ответы:

187

Как насчет понимания слов :

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}?
thefourtheye
5
@thefourtheye Я собираюсь предположить, что мой быстрее, поскольку он не требует d[k]поиска.
Джонатон Рейнхарт
Также он говорит # do somethingв комментариях, но здесь мы опускаем несколько ключей.
thefourtheye
Есть ли у нас iteritemsв Python 3? Я так не думаю. Итак, моя версия будет совместима, не так ли?
thefourtheye
1
В Python 3 вы бы заменили его iteritemsна items, что аналогично Python 2.7 iteritems.
Джонатон Рейнхарт
18

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

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

Однако, если вы действительно хотите, чтобы что-то позволяло вам перебирать отфильтрованный dict, я бы не стал выполнять двухэтапный процесс построения отфильтрованного dict и затем повторять его, а вместо этого использовал бы генератор, потому что это более питоническое (и потрясающее), чем генератор?

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

# 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()))

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

Pulkit
источник
Обратите внимание, что это 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)}
jspurim
источник
3
Мой метод использования iteritems()будет более эффективным, чем items().
Джонатон Рейнхарт
@Jonathin Reinhart Я не знал об этом. Спасибо.
jspurim
2
Только на Python 2.7. В Python 3 есть только тот items() , который действует как Python 2.7 iteritems.
Джонатон Рейнхарт
1
Вопрос явно для python 2.7
Брендан Ф
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_function? в первом случае (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 Он не принимает два параметра, он должен принимать один параметр с двумя элементами. Существует также карта звездности, которая распаковывается на два аргумента, однако это ленивый итератор (должен быть повторен перед выполнением, т.е. results = list(starmap(...))или for result in starmap(...): ...).
nmclean