фильтровать элементы в словаре Python, где ключи содержат определенную строку

Я кодер 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


person memo    schedule 26.05.2014    source источник


Ответы (5)


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

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}
person Jonathon Reinhart    schedule 26.05.2014
comment
Почему не filtered_dict = {k:d[k] for k in d if filter_string in k}? - person thefourtheye; 26.05.2014
comment
@thefourtheye Я собираюсь догадаться, что мой быстрее, так как он не выполняет поиск d[k]. - person Jonathon Reinhart; 26.05.2014
comment
Кроме того, он говорит # do something в комментариях, но здесь мы опускаем несколько ключей. - person thefourtheye; 26.05.2014
comment
Есть ли у нас iteritems в Python 3? Я так не думаю. Итак, моя версия будет совместима, не так ли? - person thefourtheye; 26.05.2014
comment
В Python 3 вы должны заменить iteritems на items, что совпадает с iteritems в Python 2.7. - person Jonathon Reinhart; 26.05.2014

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

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

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

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

# 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

Вкратце: генераторы потрясающие.

person Brendan F    schedule 26.05.2014

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

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

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

person Pulkit    schedule 19.03.2018
comment
Обратите внимание, что items: должно быть item: в определении лямбда. - person bkribbs; 01.09.2018
comment
Спасибо @bkribbs за указание на ошибку. Я исправил это сейчас. - person Pulkit; 03.09.2018

Джонатон дал вам подход с использованием понимания dict в своем ответе. Вот подход, который касается вашей части сделать что-нибудь.

Если вы хотите что-то сделать со значениями словаря, вам вообще не нужно понимание словаря:

Я использую iteritems(), так как вы пометили свой вопрос тегом python-2.7

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]
person Burhan Khalid    schedule 26.05.2014
comment
интересный. как будет определена функция some_function? в первом случае (k,v) требуется ли всего два параметра? сначала ключ, затем значение? - person memo; 26.05.2014
comment
Да просто вызываемый. Итак, map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2)) — это даст вам [4]. - person Burhan Khalid; 26.05.2014
comment
Это правильно, но более питоническим, чем использование map, является понимание списка. [f(v) for k, v in d.iteritems() if substring in k] Я думаю, что это намного читабельнее и эффективнее. - person Davidmh; 26.05.2014
comment
@memo Он не будет принимать два параметра, он будет принимать один параметр с двумя элементами. Существует также starmap, который, однако, распаковывается в два аргумента. это ленивый итератор (должен повторяться перед выполнением, т.е. results = list(starmap(...)) или for result in starmap(...): ...). - person nmclean; 26.05.2014

person    schedule
comment
Мой метод с использованием iteritems() будет более эффективным, чем items(). - person Jonathon Reinhart; 26.05.2014
comment
@ Джонатин Рейнхарт Я не знал об этом. Спасибо. - person jspurim; 26.05.2014
comment
Только на Python 2.7. В Python 3 есть только items(), который действует как iteritems в Python 2.7. - person Jonathon Reinhart; 26.05.2014
comment
Вопрос явно для python 2.7 - person Brendan F; 26.05.2014