Шаг 9.
Python: сборник рецептов.
Структуры данных и алгоритмы. Вычисления со словарями

    На этом шаге мы рассмотрим особенности реализации таких вычислений.

Задача

    Вы хотите проводить различные вычисления (например, поиск минимального и максимального значений, сортировку) на словаре с данными.

Решение

    Рассмотрим словарь, который отображает тикеры (идентификаторы акций на бирже) на цены:

prices = {
  'ACME': 45.23,
  'AAPL': 612.78,
  'IBM': 205.55,
  'HPQ': 37.20,
  'FB': 10.75
}

    Чтобы выполнить вычисления на содержимом словаря, часто бывает полезно обратить ключи и значения, используя функцию zip(). Например, вот так можно найти минимальную и максимальную цены, а также соответствующий тикер:

min_price = min(zip(prices.values(), prices.keys()))
# min_price - (10.75, 'FB')
max_price = max(zip(prices.values(), prices.keys()))
# max_price - (612.78, 'AAPL')

    Похожим образом для ранжирования данных можно использовать zip() с sorted(), как показано ниже:

prices_sorted = sorted(zip(prices.values(), prices.keys()))
# prices_sorted - [(10.75, 'FB'), (37.2, 'HPQ'),
#    (45.23, 'ACME'), (205.55, 'IBM'),
#    (612.78, 'AAPL')]

    Когда вы производите эти вычисления, обратите внимание, что zip() создает итератор, по которому можно пройти только один раз. Например, следующий фрагмент кода - неправильный:

prices_and_names = zip(prices.values(), prices.keys()) 
print(min(prices_and_names)) # OK
print(max(prices_and_names)) # ValueError: max() arg is an empty sequence


Обсуждение

    Если вы попытаетесь выполнить обычные функции обработки данных на словаре, то обнаружите, что они обрабатывают лишь ключи, но не значения. Например:

min(prices) # Вернет 'AAPL' 
max(prices) # Вернет 'IBM'

    Вероятно, это не то, чего вы хотели добиться, поскольку пытались выполнить вычисления с использованием значений словаря. Вы можете попробовать исправить это, используя метод словаря values():

min(prices.values()) # Вернет 10.75 
max(prices.values()) # Вернет 612.78

    К несчастью, в большинстве случаев это не то, чего вы хотите. Например, вам нужно знать соответствующие ключи (т. е. понять, у каких акций самая низкая цена).

    Вы можете получить ключ, соответствующий минимальному или максимальному значению, если передадите функцию в функции min() и max(). Например:

min(prices, key=lambda k: prices[k]) # Вернет 'FB' 
max(prices, key=lambda k: prices[k]) # Вернет 'AAPL'

    Однако чтобы получить минимальное значение, вам потребуется дополнительное обращение. Например:

min_value = prices[min(prices, key=lambda k: prices[k])]

    Использование функции zip() решает задачу путем "обращения" словаря в последовательность пар (value, key). Когда выполняется сравнение таких кортежей, элемент value сравнивается первым, а key - следующим. Это дает вам то самое поведение, которое вы хотите, а также позволяет проводить обработки и сортировку словаря с использованием единственного выражения.

    Стоит отметить, что в вычислениях с использованием пар (value, key) будет применен key, чтобы определить результат в экземплярах, где множественные записи имеют одинаковые value. Например, в вычислениях min() и max() запись с наименьшим или наибольшим ключом будет возвращена, если найдутся одинаковые значения. Например:

>>> prices = {'AAA': 45.23, 'ZZZ': 45.23}
>>> min(zip(prices.values(), prices.keys()))
(45.23, 'AAA')
>>> max(zip(prices.values(), prices.keys()))
(45.23, 'ZZZ')
>>> 

    На следующем шаге мы рассмотрим поиск общих элементов в двух словарях.




Предыдущий шаг Содержание Следующий шаг