Python цикл по ключам словаря

Словарь (dictionary, dict) – это ассоциативный массив, который позволяет сохранять значения по ключам.
Это очень важная, даже можно сказать основополагающая структура данных, которая используется в Python под капотом буквально повсюду: модули, классы, объекты, locals(), globals() – все это так или иначе работает лишь благодаря словарям.
Кроме того, словарь отлично подходит для решения множества прикладных задач, обладает хорошей вычислительной сложностью операций, так что и в вашем коде, наверняка, словари будут встречаться достаточно часто.
В Python большое внимание уделяется циклам. Правильно написанный заголовок цикла содержит много ценной информации: по чему итерируемся и какие данные будут использоваться в теле цикла. Это помогает читателю понять (или хотя бы предположить), что именно будет производиться в теле цикла, даже не смотря в него. Неправильно написанный цикл, который не выражает напрямую задумку автора, наоборот, сбивает читателя с толку и заставляет читать код целиком, возможно, даже не один раз.
Есть несколько способов обойти словарь в цикле. Очень важно научиться выбирать наиболее подходящий.
Объявим словарь с отношением различных валют к российскому рублю, который нам по какой-то причине нужно обойти:
currencies = {“rub”: 1, “usd”: 69.78, “eur”: 78.28}
Самый очевидный вариант обхода словаря – это попытаться напрямую запустить цикл for по объекту словаря, так же как мы делаем это со списками, кортежами, строками и любыми другими итерируемыми объектами.
for something in currencies: (something)
Словарь и правда поддерживает протокол итераций, но словарь не так прост, как другие объекты, которые мы упомянули выше. Словарь состоит из нескольких частей, ведь словарь – это отношение между ключами и значениями. Получается, что теоретически цикл по словарю может получать либо ключи, либо значения, либо пары (ключ, значение). Попробуете угадать, что же именно выведет код выше?
А выведет он следующее:
То есть обход словаря в цикле будет возвращать только ключи этого словаря.
Пожалуй, задать такое поведение по умолчанию – это очень логичное решение со стороны разработчиков Python. Было бы намного внезапнее, если бы цикл по словарю получал значения. Вариант с кортежами (ключ, значение) в качестве поведения по умолчанию мне кажется не таким уж плохим, но имеем то, что имеем.
Есть куча задач, в которых нужно обойти лишь ключи словаря, и это отличное решение для таких задач. У этого способа есть один крупный недостаток: нужно знать как работают словари. По коду совершенно неясно, что будет обходиться в цикле – ключи, значения или пары, а читатель может либо этого не знать, либо забыть, и в итоге неправильно интерпретировать код. Поэтому во избежание неоднозначности даже для обхода ключей словаря я рекомендую использовать следующий способ.
Давайте представим, что нам нужно нарисовать какую-нибудь таблицу с валютами, и для создания шапки этой таблицы нужно получить список всех валют. Значения словаря нас не интересуют, только ключи.
У словаря есть метод .keys(), который возвращает представление словаря (dict view), возвращающее ключи.
Что такое представление словаря? Это некий объект, который предоставляет доступ к данным в словаре, либо к части этих данных, и работает по следующим принципам:
- не копирует содержимое словаря, а обращается к нему динамически, на больших словарях это здорово экономит память и улучшает скорость работы программы;
- если словарь изменяется, то эти изменения автоматически становятся доступными и через представление словаря;
- не является списком, не поддерживает извлечение элементов по индексам;
- является итерируемым объектом, можно использовать в циклах сколько угодно раз.
Создадим такое представление словаря по ключам:
dict_keys = currencies.keys() (dict_keys) # dict_keys([‘rub’, ‘usd’, ‘eur’])
Давайте добавим новый ключ в словарь:
currencies[“jpy”] = 0.65 (dict_keys) # dict_keys([‘rub’, ‘usd’, ‘eur’, ‘jpy’])
Как видите, созданное ранее представление словаря обновилось автоматически, когда обновился его словарь.
Обратите внимание, что представление словаря – это не список, а совершенно другой объект. Представление словаря не поддерживает извлечение значений по индексам:
dict_keys[0] # Traceback (most recent call last): # File “<stdin>”, line 1, in <module> # TypeError: ‘dict_keys’ object is not ptable
Зато представление словаря является итерируемым объектом и его без проблем можно обходить при помощи цикла:
for key in currencies.keys(): (key) # rub # usd # eur # jpy
Результат тот же самый, что и в предыдущем способе обхода словаря, но в этот раз явно видно, что в цикле будут обрабатываться только ключи словаря.
Обратите внимание, что если в цикле вам нужны не только ключи словаря, но и значения, то обходить словарь таким образом – не самое эффективное решение. Смотрите дальше, как можно обойти словарь, чтобы получать и ключи, и значения.
По аналогии с ключами, из словаря можно извлечь только значения, без ключей. Это делается через метод словарей .values(), который возвращает представление словаря, содержащее только значения.
Это представление работает по тем же правилам, что и возвращаемое методом .keys().
Вот как можно обойти в цикле только значения словаря, без ключей:
for value in currencies.values(): (value) # 1 # 69.78 # 78.28 # 0.65
По значениям словаря уже невозможно получить ключи (ну, вообще можно попытаться, но для этого потребуется полный перебор словаря, и не факт, что ключи будут восстановлены правильно). Этот способ подойдёт только если в цикле используются исключительно значения словаря, а ключи не нужны.
Пожалуй, это самый распространённый случай. Во многих задачах, где выполняется обход словаря, в цикле используются и ключи, и соответствующие им значения.
Специально для этого у словарей есть метод .items(), который возвращает представление словаря, содержащее кортежи из двух элементов, вида (ключ, значение).
Это представление работает по точно таким же правилам, как .keys() и .values(). Единственное отличие этого представления от предыдущих состоит в том, что оно возвращает не единичные значения, а кортежи из двух значений.
for item in currencies.items(): # item – это кортеж (ключ, значение) (item[0], item[1]) # rub 1 # usd 69.78 # eur 78.28 # jpy 0.65
В Python есть возможность распаковывать итерируемые объекты, такие как кортежи, в различные переменные. Давайте на примере посмотрим как это работает:
point = (1, 2, 3) x, y, z = point (x) # 1 (y) # 2 (z) # 3
Таким образом можно распаковывать последовательности любого размера. Это намного проще, чем извлекать значения по индексам и присваивать в отдельные переменные. Этот приём можно использовать практически в любом месте программы, в том числе и в заголовке цикла.
Вот так можно обойти ключи и значения словаря, сохраняя ключ и значение в разные переменные прямо в заголовке цикла при помощи распаковки кортежа:
for key, value in currencies.items(): (key, value) # rub 1 # usd 69.78 # eur 78.28 # jpy 0.65
При обходе словаря стоит руководствоваться следующей логикой:
- если в цикле используются и ключи, и значения словаря, то нужно использовать метод .items();
- если в цикле используются только значения словаря, а ключи не важны, то нужно использовать метод .values();
- если в цикле нужны ключи словаря и ничего больше, то нужно использовать метод .keys().
Идеоматичный код проще читается и, как правило, работает быстрее.
Посмотрите запись классического выступления Реймонда Хеттингера, где он рассказывает про написание идеоматичного код. Много внимания уделяется циклам и словарям.
Обратите внимание, что это запись выступления от 2013 года, когда ещё вовсю был в ходу Python 2. В выступлении часто сравнивается Python 2 и Python 3. Не запутайтесь.
Если понравилась статья, то подпишитесь на уведомления о новых постах в блоге, чтобы ничего не пропустить!
- очень подробный разбор по обходу словарей на RealPython;
- документация про представления словарей;
- и, конечно же, посмотрите выступление Реймонда Хеттингера, обожаю этого чувака.
Источник
Иногда сложно найти в Сети правильные, а главное актуальные «best practices» для языка. Документация, конечно же, содержит всю необходимую информацию, но отсеять нужную вещь в абзацах подробного (на то она и документация) описания довольно сложно. Но недавно мне улыбнулся поиск Google, и я наткнулся на очень полезные «паттерны» языка Python от одного из core разработчиков – Raymond Hettinger.
Примечание: Все рекомендации даны в нескольких вариантах: сначала идут самые «плохие» варианты, а дальше предлагается лучшая альтернатива. Актуально для версии языка 2.7, отличия для версии 3.3 читайте в примечаниях к конкретному «паттерну».
Цикл по массиву из чисел
Плохо: иногда пишут так.
for i in [0, 1, 2, 3, 4, 5]: i**2
Хорошо: лучший, с генератором. Но в 32 битной системе список из миллиона чисел будет занимать ~ 32 mb памяти.
for i in range(6): i**2
Отлично: самый лучший вариант. В отличии от второго xrange возвращает только одно значение за раз, и не нужно лишнюю память для хранения всего массива.
for i in xrange(6): i**2
Примечание: В версии Python 3.3 xrange уже в ядре и называеться просто range.
Цикл по списку
Плохо: часто бывшие С программисты пишут так.
colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] for i in range(len(colors)): colors[i]
Хорошо: лучший вариант.
colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] for color in colors: color
Но если нужно пройти по списку задом на перед?
Плохо: опять, прошло из C дает о себе знать:
colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] for i in range(len(colors)-1, -1, -1): colors[i]
Хорошо: но в Python пишут вот так:
colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] for color in reversed(colors): color
Цикл по списку с индексами
Плохо тоже что и выше.
colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] for i in range(len(colors)): i, ‘–>’, colors[i]
Хорошо: более элегантный вариант:
colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] for i, color in enumerate(colors): i, ‘–>’, color
Цикл по двум спискам
Плохо тоже что и выше.
names = [‘raymond’, ‘rachel’, ‘matthew’] colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] n = min(len(names), len(colors)) for i in range(n): names[i], ‘–>’, colors[i]
Хорошо: с двух списков делаем один список кортежей. Проблема в том что zip использует больше памяти чем первый вариант.
names = [‘raymond’, ‘rachel’, ‘matthew’] colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] for name, color in zip(names, colors): name, ‘–>’, color
Отлично: в отличии от zip, izip использует кэширование, что помогает существенно сэкономить память.
names = [‘raymond’, ‘rachel’, ‘matthew’] colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] for name, color in izip(names, colors): name, ‘–>’, color
Примечание: В версии Python 3.3 izip вписан в ядро и называется просто zip.
Сортировка списка по алгоритму
Плохо: используя функцию для сравнения.
colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] def compare_length(c1, c2): if len(c1) < len(c2): return -1 if len(c1) > len(c2): return 1 return 0 sorted(colors, cmp=compare_length)
Хорошо: используя сортировку по ключу. Использует намного меньше памяти.
colors = [‘red’, ‘green’, ‘blue’, ‘yellow’] sorted(colors, key=len)
Примечание: Метод cmp убран с ядра Python 3.x.
Цикл по ключам словаря
Обычный способ возвращает ключи. При таком цикле происходит итерация словаря, поэтому в процессе его изменять нельзя.
for k in d: k
Для изменения словаря в цикле используйте цикл по ключам (Пример: удаление всех ключей начинающихся с R):
for k in d.keys(): if k.startswith(‘R’): del d[k]
В этом случае d.keys() делает копию ключей словаря, что позволяет нам свободно работать с оригинальной структурой.
Цикл по ключам и значением словаря
Плохо: цикл по ключам и возвращение значение по последним. Медленный способ:
for k in d: k, ‘–>’, d[k]
Хорошо: быстрее делать цикл по значениях:
for k, v in d.items(): k, ‘–>’, v
Отлично: Но самый лучший и быстрый способ это использовать итератор:
for k, v in d.iteritems(): k, ‘–>’, v
Соединение двух списков в один словарь
Очень быстрый метод, используется только один кортеж для генерации словаря.
names = [‘raymond’, ‘rachel’, ‘matthew’] colors = [‘red’, ‘green’, ‘blue’] d = dict(izip(names, colors)) # d будет иметь следующее значение: # {‘matthew’: ‘blue’, ‘rachel’: ‘green’, ‘raymond’: ‘red’}
Подсчет элементов в словаре
Плохо: обычный способ:
colors = [‘red’, ‘green’, ‘red’, ‘blue’, ‘green’, ‘red’] d = {} for color in colors: if color not in d: d[color] = 0 d[color] += 1 #{‘blue’: 1, ‘green’: 2, ‘red’: 3}
Хорошо: использует функцию get():
colors = [‘red’, ‘green’, ‘red’, ‘blue’, ‘green’, ‘red’] d = {} for color in colors: d[color] = d.get(color, 0) + 1
Отлично: самый продвинутый способ это использовать defaultdict(). Но вы должны знать как он работает.
d = defaultdict(int) for color in colors: d[color] += 1
Группирование элементов списка
Плохо: если нужно сгруппировать элементы списка по некоторому признаку (в примере – длина строки) часто используют такой метод:
names = [‘raymond’, ‘rachel’, ‘matthew’, ‘roger’, ‘betty’, ‘melissa’, ‘judith’, ‘charlie’] d = {} for name in names: key = len(name) if key not in d: d[key] = [] d[key].append(name) {5: [‘roger’, ‘betty’], 6: [‘rachel’, ‘judith’], 7: [‘raymond’, ‘matthew’, ‘melissa’, ‘charlie’]}
Хорошо: но есть способ гораздо элегантней и быстрее:
d = defaultdict(list) for name in names: key = len(name) d[key].append(name)
Итог
На сегодня все. Надеюсь эти тривиальные, но полезные примеры помогут кому-то улучшить свой код, как они помогли это сделать мне. Их автором является Raymond Hettinger (@raymondh), Python Core Developer.
UPD: Видео доклада на PyCon 2013, с которого были взяты примеры кода: https://youtu.be/OSGv2VnC0go (на английском), содержит еще очень много интересной информации не перенесенной в статью, а также юмор и харизму докладчика.
Источник
Подробное руководство по использованию разных способов перебора содержимого словаря в Python
Введение
Словари являются одной из самых популярных структур данных используемых на любом этапе разработки программного обеспечения. Словарь позволяет нам хранить наши данные в удобном формате ключ:значение, что в свою очередь, дает нам возможность получать быстрый доступ к данным внутри словаря.
Использование метода keys
Словари в Python имеют удобный метод, который позволяет нам легко перебирать все инициализированные ключи.
Имейте в виду, что начиная с Python 3 этот метод не возвращает список, а возвращает объект представления. Объект представления – это возвращаемое значение исходного объекта, изменения которого не влияют на исходный объект. Для внесения изменений и сохранения данных в измененном объекте представления необходимо присвоить его какой-либо переменной.
Давайте посмотрим, как это работает:
# В качестве словаря используем рейтинг популярности языков программирования TIOBE tiobe_index = { “C”: 17.38, “Java”: 11.96, “Python”: 11.72, “C++”: 7.56, “C#”: 3.95, } # Выведем на экран список ключей словаря и тип объекта возвращаемый выполнением метода keys (f”Key view: {tiobe_index.keys()}”) (f”Type: {type(tiobe_index.keys())}”)
Результат выполнения кода
Key view: dict_keys([‘C’, ‘Java’, ‘Python’, ‘C++’, ‘C#’]) Type: <class ‘dict_keys’>
Итерация словаря по ключу в цикле for с указанием индекса элемента
for k in tiobe_index.keys(): (f”{k}: {tiobe_index[k]}”)
Результат выполнения кода
C: 17.38 Java: 11.96 Python: 11.72 C++: 7.56 C#: 3.95
При использовании ключевого слова in в цикле for словарь вызывает свой метод iter. Затем этот метод возвращает итератор, который используется для неявного просмотра ключей предоставленного словаря.
Использование метода value
Так же как и метод keys, метод value возвращает объект представления, но вместо итерации по ключу, он итератует по значениям:
# В качестве словаря используем рейтинг популярности языков программирования TIOBE tiobe_index = { “C”: 17.38, “Java”: 11.96, “Python”: 11.72, “C++”: 7.56, “C#”: 3.95, } # Выведем на экран список значений словаря и тип объекта возвращаемый выполнением метода value (f”Value view: {tiobe_index.values()}”) (f”Type: {type(tiobe_index.values())}”)
Результат выполнения кода
Value view: dict_values([17.38, 11.96, 11.72, 7.56, 3.95]) Type: class <‘dict_values’>
Итерация словаря по значению в цикле for
for k in tiobe_index.values(): (f”{k}”)
Результат выполнения кода
17.38 11.96 11.72 7.56 3.95
В отличие от предыдущего метода, этот метод предоставляет только значения. Он полезен, когда вас не интересует содержимое ключей в словаре.
Использование метода items
Так же как и методы keys и values, метод items возвращает объект представления, но вместо того, чтобы просто выполнять итерации по ключам или значениям, он выполняет итерации по индексу словарю (ключу и значению одновременно).
Давайте посмотрим как это работает
# В качестве словаря используем рейтинг популярности языков программирования TIOBE tiobe_index = { “C”: 17.38, “Java”: 11.96, “Python”: 11.72, “C++”: 7.56, “C#”: 3.95, } # Выведем на экран список элементов словаря и тип объекта возвращаемый выполнением метода items (f”Items view: {tiobe_index.items()}”) (f”Type: {type(tiobe_index.items())}”)
Результат выполнения кода
Items view: dict_items([(‘C’, 17.38), (‘Java’, 11.96), (‘Python’, 11.72), (‘C++’, 7.56), (‘C#’, 3.95)]) Type: <class ‘dict_items’>
Итерация словаря по паре клюк:значение происходит следующим образом
for k, v in tiobe_index.items(): (f”key: {k}, value: {v}”)
Результат выполнения кода
key: C, value: 17.38 key: Java, value: 11.96 key: Python, value: 11.72 key: C++, value: 7.56 key: C#, value: 3.95
Важно отметить, что в старых версиях Python, где items, keys и values возвращали копию данных из словаря, Python 3 возвращает объект представления.
Они более эффективны, так как обеспечивают динамическое представление и кроме того, при внесении изменений в исходный словарь, они сразу же отражаются в объекте представления (и наоборот).
Заключение
В этой статье мы рассмотрели различные способы итерации по словарю в Python используя методы keys(), values() и items().
Если вы ищите способ системно подойти к обучению языка программирования Python, рекомендую записаться на курсы онлайн обучения.
Поделиться записью в социальных сетях
Источник
Это четвертый пост об идиомах в Питона. Теперь пришло время узнать, что же такое словари в Python. Вы наверняка знаете, что это такая структура данных, тип которой обычно обозначают как dict. Пост же несколько подробнее расскажет о словарях: о том, как их перебирать или получать значение по ключу.
Работа со словарями Python
Вообще, словарями в Python называют коллекции произвольных объектов с доступом по ключу. При этом коллекции неупорядоченные. По-другому словари можно называть ассоциативными массивами или хеш-таблицами. Словарь может выглядеть, например, так:
dict = {‘ключ1’: 1, ‘ключ2’: 2}
Конечно же, существует куча способов работы со словарями, и все они не поместятся в этот пост. Но некоторые полезные идиомы, пожалуй, стоит упомянуть.
Цикл по ключам словаря Python
Одна из базовых операций, которая требуется при работе со словарями – это цикл по его ключам. Наверняка вы будете часто использовать такую операцию, поэтому стоит обратить внимание на правильный и красивый способ ее выполнения.
#Не перебирайте ключи так for k in dic.keys(): (k) #Делайте это так for k in dic: (k)
Как видите. для цикла по ключам словаря не нужно использовать метод dictionary.keys(). Все что нужно – это ссылка на словарь.
Цикл по паре ключ-значение Python
Еще одна нужная операция, которая почти всегда требуется при работе со словарями – это цикл по паре ключ:значение. Конечно же, в Python есть несколько быстрых и простых способ построить такой цикл.
#цикл можно построить так for k in dic: (k) (dic[k]) #или вот так for k, val in dic.items(): (k) (val)
В примере показано два способа перебора пар ключ-значение в словаре. Первый перебирает ключи словаря, а значения извлекает по ключу. Второй пример пробегает по словарю, распаковывая ключи и значения в две переменные.
Использование dictionary.get() для получения значений
Если нужно получить значение по ключу, но при этом неизвестно, существует такой ключ или нет – используйте метод dictionary.get().
#Использование get() для получения значения val = dic.get(‘key1’, ‘na’)
Если ключ «key1» существует в словаре dic, то переменной будет присвоено значение в соответствии с ключом. В противном случае переменная получит значение второго аргумента функции get().
Удаление элементов из словаря Python по критериям
Вероятно, если бы перед вами встала такая задача, то в мыслях сразу бы возникли циклы и условные операторы. Но в Питоне все это не требуется! Смотрите:
#Удаление элементов из словаря по критериям dic = {k : dic[k] for k in dic if not len(k) < 5}
Синтаксис очень простой: {ключ : значение for ключ in словарь [условия]}. Пример выше создаст новый словарь, которые содержит все пары ключ-значение, в которых ключ имеет длину менее 5.
Объединение двух списков в словарь
Например, у вас есть список имен и список фамилий. Но вы хотите иметь словарь из пар фамилия-имя. Что делать в такой ситуации? Объединять списки в словарь, конечно же!
#Объединение двух списков в словарь f_names = [“Ivan”, “Anton”, “Oleg”, “Petr”] l_names = [“Petrov”, “Sidorov”, “Ivanov”, “Skripkin”] names = dict(zip(f_names, l_names))
Эта идиома принимает на вход два списка: f_names и l_names, а затем формирует из них словарь из пар фамилия-имя. Это быстро и просто, как и в других идиомах Python. Если вас заинтересует метод zip() – почитайте о нем подробнее в документации.
На этом все. Надеюсь, несколько описанных идиом помогут вам эффективнее использовать словари в Python, сделать ваш код более читабельным и элегантным. Кстати, если интересно – можете почитать еще об одной структуре данных в Питоне – о списках.
Спасибо Jamal Moir за замечательные советы.
Источник