Примеры использования словарей

Словари очень удобны для подсчета уникальных значений в последовательности, например, чтобы узнать количество уникальных букв (или количество вхождений каждой буквы) в строке 'Лев Николаевич Толстой' то это можно выполнить следующим образом.

\(1)\) Сначала создаем пустой словарь, в котором будет сохраняться информация о подсчете. Ключами данного словаря будут буквы, попадающиеся в фразе, а значениями будет количество их вхождений:

s_n = {}

\(2)\) Теперь нужно перебрать каждый символ фразы в цикле for...:

for i in 'Лев Николаевич Толстой':

\(3)\) После того как в переменной i появился первый символ, нужно проверить наличие в словаре s_n элемента с таким же ключом.

    if i not in s_n:

\(3)\) На самой первой итерации в переменной i окажется символ 'Л', но так как к этому моменту словарь s_n вообще пуст, то выражение i not in s_n вернет True. Это означает что подобный символ встречен впервые, а значит нужно создать в словаре соответствующий элемент с ключом i (сейчас i = 'Л') и значением \(1\):

        s_n[i] = 1

\(3)\) Когда последовательный перебор символов доберется до буквы 'е' в отчестве великого писателя, выражение i not in s_n вернет False. Это будет означать что подобная буква уже встречалась ранее, а значит элемент с таким ключом уже существует в s_n, поэтому его значение нужно увеличить на \(1\) в альтернативной ветке else:

    else:
        s_n[i] += 1

Все. Если собрать весь код воедино, то получится:

>>> s_n = {}
>>>
>>> for i in 'Лев Николаевич Толстой':
    if i not in s_n:
        s_n[i] = 1
    else:
        s_n[i] += 1

Взглянуть на результат, можно выполнив печать каждой пары ключ-значение:

>>> for i in s_n.items():
...     print(i)
...
('Л', 1)
('е', 2)
('в', 2)
(' ', 2)
('Н', 1)
('и', 2)
('к', 1)
('о', 3)
('л', 2)
('а', 1)
('ч', 1)
('Т', 1)
('с', 1)
('т', 1)
('й', 1)

Если воспользоваться методом get(), то код для подсчета станет еще короче:

>>> s_n = {}
>>>
>>> for i in 'Лев Николаевич Толстой':
...     s_n[i] = s_n.get(i, 0) + 1

Другой пример, похож на предыдущий, но показывает возможный способ подсчета различных комбинаций. Задача заключается в следующем: найти натуральные числа, которые могут быть представлены суммой квадратов более чем одним способом, т.е. по сути решить диофантово уравнение \(a^{2} + b^{2} = c^{2} + d^{2}\):

>>> sum_sq = {}
>>> res = {}
>>>
>>> for i in range(1, 10):
...         for j in range(1, i + 1):
...             sq = i**2 + j**2
...             if sq not in sum_sq:
...                 sum_sq[sq] = [(i, j)]
...             else:
...                 sum_sq[sq].append((i, j))
...                 if len(sum_sq[sq]) > 1:
...                     res[sq] = sum_sq[sq]
...
>>> res
{50: [(5, 5), (7, 1)], 65: [(7, 4), (8, 1)], 85: [(7, 6), (9, 2)]}

Как видим нашлось целых три решения, например \(7^{2}+6^{2} = 9^{2}+2^{2} = 85\).


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

>>> from random import randint
>>>
>>> tmp_cache = {}
>>> n = 0
>>>
>>> for i in range(100000):
...     c = (randint(1, 20), randint(1, 20))
...     if c not in tmp_cache:
...         tmp_cache[c] = c[0]**3 - c[1]**2
...         n += 1
...
>>> n
400

Как видим, на вход было подано \(100000\) пар натуральных чисел, но при этом выражение вычислялось всего \(400\) раз. Данный пример, крайне искусственный, но нечто похожее может встретиться и в реальной жизни. Например, если вы вычисляете кратчайший маршрут между двумя локациями на карте (парами вершин в графе), то лучше хранить вычисленные значения, т.к. для популярных локаций запросы однозначно будут повторяться, а вычисление расстояний - это крайне накланый процесс.

Разреженные матрицы так же удобнее хранить в виде словарей. Например матрица:

В общем словари удобны в тех ситуациях когда есть какието связанные пары. К примеру имя файла и его размер:

>>> import os
>>>
>>> F = {name: os.path.getsize(name) for name in os.listdir(".")}
>>>
>>> for i in F.items():
...     print(i)
...
('DLLs', 8192)
('Doc', 0)
('include', 32768)
('Lib', 65536)
('libs', 0)
('LICENSE.txt', 31453)
('NEWS.txt', 918380)
('python.exe', 97352)
('python3.dll', 58952)
('python38.dll', 4051016)
('pythonw.exe', 95816)
('Scripts', 4096)
('tcl', 4096)
('Tools', 0)
('vcruntime140.dll', 83744)