Все курсы > Программирование на Питоне > Занятие 8
Словари, наряду со списками, кортежами и множествами, также входят в состав коллекций (collections), т.е. структур данных, содержащих несколько элементов. Мы уже начали изучать словари на пятом занятии вводного курса. Продолжим этот путь.
Откроем ноутбук к этому занятию⧉
Понятие словаря в Питоне
Словарь — неупорядоченный набор элементов с доступом по ключу.

Создание словаря
Пустой словарь можно инициализировать через фигурные скобки {} или функцию dict().
1 2 |
dict_1, dict_2 = {}, dict() print(dict_1, dict_2) |
1 |
{} {} |
Словарь можно сразу заполнить ключами и значениями.
1 2 |
company = {'name': 'Toyota', 'founded' : 1937, 'founder': 'Kiichiro Toyoda'} company |
1 |
{'founded': 1937, 'founder': 'Kiichiro Toyoda', 'name': 'Toyota'} |
Словарь можно также создать из вложенных списков.
1 2 |
tickers = dict([['TYO', 'Toyota'], ['TSLA', 'Tesla'], ['F', 'Ford']]) tickers |
1 |
{'F': 'Ford', 'TSLA': 'Tesla', 'TYO': 'Toyota'} |
Иногда бывает полезно создать словарь с заранее известными ключами и заданным значением. В этом нам поможет метод .fromkeys().
1 2 3 4 5 6 7 8 9 |
# ключи мы поместим в кортеж keys = ('k1', 'k2', 'k3') # значением каждого ключа будет 0, # если ничего не указывать, ключи получат значение None value = 0 empty_k = dict.fromkeys(keys, value) empty_k |
1 |
{'k1': 0, 'k2': 0, 'k3': 0} |
Ключи и значения словаря
Ключами словаря могут быть только неизменяемые типы данных. Например, строки, числа, кортежи или логические значения (Boolean). Кроме того, ключи должны быть уникальными и соответственно не могут повторяться.
Значения словаря, наоборот, могут состоять из чисел, строк, пропущенных (NaN) и логических значений, значения типа None, списков, массивов Numpy и вложенных словарей.
1 2 3 4 5 6 7 8 9 10 11 12 |
import numpy as np value_types = {'k1' : 123, 'k2' : 'string', 'k3' : np.NaN, # тип "Пропущенное значение" 'k4' : True, # логическое значение 'k5' : None, 'k6' : [1, 2, 3], 'k7' : np.array([1, 2, 3]), 'k8' : {1 : 'v1', 2 : 'v2', 3 : 'v3'}} value_types |
1 2 3 4 5 6 7 8 |
{'k1': 123, 'k2': 'string', 'k3': nan, 'k4': True, 'k5': None, 'k6': [1, 2, 3], 'k7': array([1, 2, 3]), 'k8': {1: 'v1', 2: 'v2', 3: 'v3'}} |
Методы .keys(), .values() и .items()
Создадим словарь с данными сотрудника.
1 2 3 4 |
person = {'first name' : 'Иван', 'last name' : 'Иванов', 'born' : 1980, 'dept' : 'IT'} |
Как мы уже знаем, доступ к ключам и значениям можно получить через методы .keys() и .values() соответственно.
1 |
person.keys() |
1 |
dict_keys(['first name', 'last name', 'born', 'dept']) |
1 |
person.values() |
1 |
dict_values(['Иван', 'Иванов', 1980, 'IT']) |
Метод .items() возвращает и то, и другое в формате списка из кортежей.
1 |
person.items() |
1 |
dict_items([('first name', 'Иван'), ('last name', 'Иванов'), ('born', 1980), ('dept', 'IT')]) |
Использование цикла for
Ключи и значения словаря удобно просматривать с помощью цикла for и метода .items().
1 2 |
for k, v in person.items(): print(k, v) |
1 2 3 4 |
first name Иван last name Иванов born 1980 dept IT |
Как мы уже знаем, если записать результат метода .items() в одну переменную, будут выведены кортежи из ключа и значения.
Использование в цикле for методов .keys() и .values() выводит только ключи или только значения соответственно.
Доступ по ключу и метод .get()
Конкретное значение в словаре можно получить, введя название словаря и затем название ключа в квадратных скобках.
1 |
person['last name'] |
1 |
'Иванов' |
Если такого ключа нет, Питон выдаст ошибку.
1 |
person['education'] |

Для того чтобы этого не произошло, можно использовать метод .get(). Он также выводит значение по ключу.
1 |
person.get('born') |
1 |
1980 |
Если ключа в словаре нет, метод .get() возвращает значение None.
1 |
print(person.get('education')) |
1 |
None |
Проверка наличия ключа и значения в словаре
С помощью оператора in мы можем проверить наличие определенного ключа в словаре.
1 |
'born' in person |
1 |
True |
Важно сказать, что оператор in работает быстрее метода .get().
Метод .values() поможет проверить наличие определенного значения.
1 |
1980 in person.values() |
1 |
True |
Метод .items() поможет проверить наличие пары ключ : значение. Обратите внимание, эту пару мы записываем в форме кортежа.
1 |
('born', 1980) in person.items() |
1 |
True |
Операции со словарями
Добавление и изменение элементов
Добавить элемент можно, передав новому ключу новое значение.
1 2 3 |
# обратите внимание, в данном случае новое значение - это список person['languages'] = ['Python', 'C++'] person |
1 2 3 4 5 |
{'born': 1980, 'dept': 'IT', 'first name': 'Иван', 'languages': ['Python', 'C++'], 'last name': 'Иванов'} |
Изменить элемент можно передав существующему ключу новое значение.
1 2 3 |
# значение - это по-прежнему список, но из одного элемента person['languages'] = ['Python'] person |
1 2 3 4 5 |
{'born': 1980, 'dept': 'IT', 'first name': 'Иван', 'languages': ['Python'], 'last name': 'Иванов'} |
Метод .update() позволяет соединить два словаря.
1 2 3 4 5 6 |
# возьмем еще один словарь new_elements = {'job' : 'программист', 'experience' : 7} # и присоединим его к существующему словарю с помощью метода .update() person.update(new_elements) person |
1 2 3 4 5 6 7 |
{'born': 1980, 'dept': 'IT', 'experience': 7, 'first name': 'Иван', 'job': 'программист', 'languages': ['Python'], 'last name': 'Иванов'} |
Метод .setdefault() не изменяет значение, если указанный ключ уже содержится в словаре.
1 2 |
person.setdefault('last name', 'Петров') person |
1 2 3 4 5 6 7 |
{'born': 1980, 'dept': 'IT', 'experience': 7, 'first name': 'Иван', 'job': 'программист', 'languages': ['Python'], 'last name': 'Иванов'} |
Если такого ключа нет, ключ и соответствующее значение будут добавлены в словарь.
1 2 |
person.setdefault('f_languages', ['русский', 'английский']) person |
1 2 3 4 5 6 7 8 |
{'born': 1980, 'dept': 'IT', 'experience': 7, 'f_languages': ['русский', 'английский'], 'first name': 'Иван', 'job': 'программист', 'languages': ['Python'], 'last name': 'Иванов'} |
Удаление элементов
Метод .pop() удаляет элемент по ключу и выводит удаляемое значение.
1 |
person.pop('dept') |
1 |
'IT' |
Убедимся, что этой пары ключа и значения больше нет в словаре.
1 |
person |
1 2 3 4 5 6 7 |
{'born': 1980, 'experience': 7, 'f_languages': ['русский', 'английский'], 'first name': 'Иван', 'job': 'программист', 'languages': ['Python'], 'last name': 'Иванов'} |
Ключевое слово del также удаляет элемент по ключу.
1 2 |
# удаляемое значение не выводится del(person['born']) |
Метод .popitem() удаляет и выводит последний добавленный в словарь элемент.
1 |
person.popitem() |
1 |
('f_languages', ['русский', 'английский']) |
Метод .clear() удаляет все ключи и значения и возвращает пустой словарь.
1 2 |
person.clear() person |
1 |
{} |
Ключевое слово del также позволяет удалить словарь целиком.
1 2 3 4 5 |
# удалим весь словарь del person # если попытаться вновь вызвать эту переменную, Питон выдаст ошибку person |

Сортировка словарей
Для сортировки словарей можно использовать функцию sorted().
1 2 |
# возьмем несложный словарь dict_to_sort = {'k2' : 30, 'k1' : 20, 'k3' : 10} |
Отсортируем ключи этого словаря.
1 |
sorted(dict_to_sort) |
1 |
['k1', 'k2', 'k3'] |
Теперь отсортируем значения с помощью метода .values().
1 |
sorted(dict_to_sort.values()) |
1 |
[10, 20, 30] |
Если мы хотим отсортировать пары ключ : значение по ключу или по значению, вначале воспользуемся методом .items() для извлечения этих пар (кортежей) из словаря.
1 |
dict_to_sort.items() |
1 |
dict_items([('k2', 30), ('k1', 20), ('k3', 10)]) |
Затем мы укажем эти кортежи в качестве первого аргумента функции sorted(). Параметру key этой же функции мы передадим lambda-функцию, которая вернет либо ключ lambda x : x[0] каждого кортежа, либо его значение lambda x : x[1]. Именно по ним и будет произведена сортировка.
1 2 |
# посмотрим на сортировку по ключу sorted(dict_to_sort.items(), key = lambda x : x[0]) |
1 |
[('k1', 20), ('k2', 30), ('k3', 10)] |
1 2 |
# а теперь по значению sorted(dict_to_sort.items(), key = lambda x : x[1]) |
1 |
[('k3', 10), ('k1', 20), ('k2', 30)] |
Копирование словарей
1 2 |
# создадим исходный словарь с количеством студентов на первом и втором курсах университета original = {'Первый курс' : 174, 'Второй курс' : 131} |
Копирование объектов в Питоне (не только словарей) осуществляется с помощью метода .copy().
1 2 3 4 5 6 7 8 9 |
# создадим копию исходного словаря с помощью метода .copy() new_1 = original.copy() # добавим информацию о третьем курсе в новый словарь new_1['Третий курс'] = 117 # выведем исходный и новый словари print(original) print(new_1) |
1 2 |
{'Первый курс': 174, 'Второй курс': 131} {'Первый курс': 174, 'Второй курс': 131, 'Третий курс': 117} |
Как мы видим, исходный словарь не изменился. Так и должно быть.
Теперь давайте попробуем скопировать словарь с помощью оператора присваивания =.
1 2 3 4 5 6 7 8 9 |
# передадим исходный словарь в новую переменную new_2 = original # удалим элементы нового словаря new_2.clear() # выведем исходный и новый словари print(original) print(new_2) |
1 2 |
{} {} |
Данные были удалены из обоих словарей. Так быть не должно и поэтому такой метод копирования использовать не стоит.
Функция dir()
На сегодняшнем занятии мне также хотелось бы показать вам функцию dir(), благодаря которой можно увидеть методы того или иного объекта. Давайте посмотрим на методы словаря.
1 2 3 4 5 |
# создадим словарь и some_dict = {'k': 1} # передадим его в функцию dir() dir(some_dict) |
1 2 3 4 5 6 7 8 9 10 11 12 |
['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', ... ] |
Вначале всегда идут так называемые специальные методы. Они начинаются и заканчиваются символом двойного подчеркивания __.
Например, когда мы вызываем функцию print() и передаем ей словарь.
1 |
print(some_dict) |
1 |
{'k': 1} |
На самом деле мы применяем к словарю метод __str__().
1 |
some_dict.__str__() |
1 |
'{'k': 1}' |
В большинстве случаев нас будут интересовать методы без символов двойного подчеркивания.
1 2 |
# выведем последние 11 элементов списка dir(some_dict)[-11:] |
1 2 3 4 5 6 7 8 9 10 11 |
['clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'] |
В частности, со всеми этими методами словаря мы уже познакомились.
Dict comprehension
Dictionary comprehension, как и в случае со списками, позволяет превратить один словарь в другой. В процессе этого превращения, элементы исходного словаря могут быть изменены или отобраны на основе какого-либо условия.
Основной целью использования как list, так и dict comprehension является упрощение и повышение читаемости кода.
Приведем базовую схему dict comprehension.

Посмотрим на два несложных примера.
1 2 |
# создадим еще один несложный словарь source_dict = {'k1' : 2, 'k2' : 4, 'k3' : 6} |
В первом примере умножим каждое значение на два.
1 |
{k : v * 2 for (k, v) in source_dict.items()} |
1 |
{'k1': 4, 'k2': 8, 'k3': 12} |
Во втором примере сделаем символы всех ключей заглавными с помощью метода .upper().
1 |
{k.upper() : v for (k, v) in source_dict.items()} |
1 |
{'K1': 2, 'K2': 4, 'K3': 6} |
Теперь предположим, мы хотим отсортировать те пары, в которых значение больше двух, но меньше шести.
1 |
{k : v for (k, v) in source_dict.items() if v > 2 if v < 6} |
1 |
{'k2': 4} |
Если бы мы выполняли эту задачу с помощью цикла for, то использовали бы оператор И.
1 2 3 4 5 6 7 8 9 |
new_dict = {} for k, v in source_dict.items(): if v > 2 and v < 6: # если условия верны, записываем ключ и значение в новый словарь new_dict[k] = v new_dict |
1 |
{'k2': 4} |
Как и в случае list comprehension, применение условия if-else несколько меняет изначальную схему. Предположим, мы хотим определить, какие из значений четные, а какие нечетные.
1 |
{k : ('even' if v % 2 == 0 else 'odd') for (k, v) in source_dict.items()} |
1 |
{'k1': 'even', 'k2': 'even', 'k3': 'even'} |
Dict comprehension можно использовать вместо метода .fromkeys().
1 2 3 4 5 |
# создадим кортеж из ключей keys = ('k1', 'k2', 'k3') # передадим словарю ключи из кортежа keys и зададим значение 0 каждому из них {k : 0 for k in keys} |
1 |
{'k1': 0, 'k2': 0, 'k3': 0} |
Дополнительные примеры
lambda-функция, функции map() и zip()
Пример со списком
Возьмем список фруктов.
1 |
words = ['apple', 'banana', 'fig', 'blackberry'] |
Предположим, что мы хотим создать словарь, где ключами будут названия фруктов, а значениями — количество букв в каждом из слов.

Вначале создадим список с количеством символов. Для этого нам понадобится функция map(). Эта функция принимает два аргумента: во-первых, что нужно сделать с каждым из элементов, во-вторых, перечень этих самых элементов.

В нашем случае первым аргументом будет lambda-функция lambda word : len(word), которая просто посчитает длину передаваемого ей слова, а вторым аргументом будет наш список фруктов.
1 2 |
length = list(map(lambda word : len(word), words)) length |
1 |
[5, 6, 3, 10] |
Как вы видите, мы использовали функцию list() для преобразования результата в список.
Остается создать словарь. Для этого с помощью знакомой нам функции zip() мы создадим пары (название фрукта, количество символов) и передадим их функции dict() для создания словаря.
1 |
dict(zip(words, length)) |
1 |
{'apple': 5, 'banana': 6, 'blackberry': 10, 'fig': 3} |
Интересно, что эту же задачу можно решить с помощью list comprehension.
1 |
dict(zip(words, [len(word) for word in words])) |
В данном случае функции zip() мы передаем список фруктов и очень простую list comprehension, которая рассчитывает длину слов.
Пример со словарем
Теперь поработаем со словарями. Предположим, что мы продолжаем спрашивать людей об их росте, и у нас есть несколько американцев, которые сообщили свой рост в футах.
1 |
height_feet = {'Alex' : 6.1, 'Jerry' : 5.4, 'Ben' : 5.8} |
Наша задача — создать точно такой же словарь, но чтобы футы были преобразованы в метры. Вначале создадим список с данными о росте в метрах.
1 2 3 |
# один фут равен 0,3048 метра metres = list(map(lambda m: m * 0.3048, height_feet.values())) metres |
1 |
[1.85928, 1.6459200000000003, 1.76784] |
Здесь мы снова воспользовались lambda-функцией, которую в качестве аргумента передали в функцию map(). Обратите внимание, вторым аргументом стали значения словаря, полученные через метод .values().
После этого мы можем снова воспользоваться функцией zip(), которой передадим ключи исходного словаря и список данных о росте с округлением до двух знаков после запятой.
1 |
dict(zip(height_feet.keys(), np.round(metres, 2))) |
1 |
{'Alex': 1.86, 'Ben': 1.77, 'Jerry': 1.65} |
Как и в предыдущем примере, эту задачу можно решить с помощью dict comprehension.
1 2 |
# мы просто преобразуем значения словаря в метры {k : np.round(v * 0.3048, 2) for (k, v) in height_feet.items()} |
1 |
{'Alex': 1.86, 'Ben': 1.77, 'Jerry': 1.65} |
Код соответствует основной схеме, приведенной выше, и в целом читается проще, чем конструкция с lambda-функцией и функцией map().
Вложенные словари
Рассмотрим более сложный словарь с информацией о нескольких сотрудниках.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
employees = { 'id1': { 'first name': 'Александр', 'last name' : 'Иванов', 'age': 30, 'job':'программист' }, 'id2': { 'first name': 'Ольга', 'last name' : 'Петрова', 'age': 35, 'job':'ML-engineer' } } |
В данном случае ключами словаря выступают id сотрудников, а значениями — вложенные словари с информацией о них.
1 2 |
for v in employees.values(): print(v) |
1 2 |
{'first name': 'Александр', 'last name': 'Иванов', 'age': 30, 'job': 'программист'} {'first name': 'Ольга', 'last name': 'Петрова', 'age': 35, 'job': 'ML-engineer'} |
Базовые операции
Для того чтобы вывести значение элемента вложенного словаря, воспользуемся двойным ключом.
1 2 |
# первый ключ - нужный нам сотрудник, второй - элемент с информацией о нем employees['id1']['age'] |
1 |
30 |
Небольшое отступление от темы. Сложные структуры данных бывает удобно вывести с помощью функции pprint() одноименного модуля.
1 |
from pprint import pprint |
Функция pprint() расшифровывается как pretty print («красивая печать») и в некоторых случаях справляется со своей задачей лучше обычной функции print().
Посмотрим как можно добавить вложенный словарь.
1 2 3 4 5 |
# добавим информацию о новом сотруднике employees['id3'] = {'first name': 'Дарья', 'last name' : 'Некрасова', 'age': 27, 'job' : 'веб-дизайнер' } # и выведем обновленный словарь с помощью функции pprint() pprint(employees) |
1 2 3 4 5 6 7 8 9 10 11 12 |
{'id1': {'age': 30, 'first name': 'Александр', 'job': 'программист', 'last name': 'Иванов'}, 'id2': {'age': 35, 'first name': 'Ольга', 'job': 'ML-engineer', 'last name': 'Петрова'}, 'id3': {'age': 27, 'first name': 'Дарья', 'job': 'веб-дизайнер', 'last name': 'Некрасова'}} |
Изменить значение вложенного словаря можно также с помощью двойного ключа.
1 2 |
employees['id3']['age'] = 26 pprint(employees) |
1 2 3 4 5 6 7 8 9 10 11 12 |
{'id1': {'age': 30, 'first name': 'Александр', 'job': 'программист', 'last name': 'Иванов'}, 'id2': {'age': 35, 'first name': 'Ольга', 'job': 'ML-engineer', 'last name': 'Петрова'}, 'id3': {'age': 26, 'first name': 'Дарья', 'job': 'веб-дизайнер', 'last name': 'Некрасова'}} |
Циклы for
Посмотрим, как можно использовать цикл for со вложенными словарями. Давайте заменим тип данных с информацией о возрасте сотрудника с int на float.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# для этого вначале пройдемся по вложенным словарям, # т.е. по значениям info внешнего словаря employees for info in employees.values(): # затем по ключам и значениям вложенного словаря info for k, v in info.items(): # если ключ совпадет со словом 'age' if k == 'age': # преобразуем значение в тип float info[k] = float(v) pprint(employees) |
1 2 3 4 5 6 7 8 9 10 11 12 |
{'id1': {'age': 30.0, 'first name': 'Александр', 'job': 'программист', 'last name': 'Иванов'}, 'id2': {'age': 35.0, 'first name': 'Ольга', 'job': 'ML-engineer', 'last name': 'Петрова'}, 'id3': {'age': 26.0, 'first name': 'Дарья', 'job': 'веб-дизайнер', 'last name': 'Некрасова'}} |
Вложенные словари и dict comprehension
Вложенные словари также допускают использование dict comprehension. Предположим, что мы хотим вернуть данные о возрасте из типа float обратно в тип int. Разобьем эту задачу на несколько этапов.
Шаг 1. Просто выведем словарь employees без изменений, используя dict comprehension.
1 |
pprint({id : info for id, info in employees.items()}) |
1 2 3 4 5 6 7 8 9 10 11 12 |
{'id1': {'age': 30.0, 'first name': 'Александр', 'job': 'программист', 'last name': 'Иванов'}, 'id2': {'age': 35.0, 'first name': 'Ольга', 'job': 'ML-engineer', 'last name': 'Петрова'}, 'id3': {'age': 26.0, 'first name': 'Дарья', 'job': 'веб-дизайнер', 'last name': 'Некрасова'}} |
Шаг 2. Создадим еще один dict comprehension, с помощью которого внутри каждого вложенного словаря мы заменим тип данных значения с float на int, если ключ совпадает с age, если не сопадает — оставим значение без изменений (по сути, условие с if-else).
1 |
{k : (int(v) if k == 'age' else v) for k, v in info.items()} |
Шаг 3. Вставим второй dict comprehension в первый вместо переменной info. Напомнью, info — это значения внешнего словаря, которые сами по себе являются словарями. Именно к ним мы и применим второй dict comprehension.
1 |
pprint({id : {k : (int(v) if k == 'age' else v) for k, v in info.items()} for id, info in employees.items()}) |
1 2 3 4 5 6 7 8 9 10 11 12 |
{'id1': {'age': 30, 'first name': 'Александр', 'job': 'программист', 'last name': 'Иванов'}, 'id2': {'age': 35, 'first name': 'Ольга', 'job': 'ML-engineer', 'last name': 'Петрова'}, 'id3': {'age': 26, 'first name': 'Дарья', 'job': 'веб-дизайнер', 'last name': 'Некрасова'}} |
С поставленной задачей мы справились. Впрочем, такая сложная схема dict comprehension идет вразрез с самой идеей этого метода, которая заключается в упрощении кода.
Частота слов в тексте
Напоследок разберем очень несложный пример подсчета частоты слов в тексте. Это уже знакомый нам мешок слов (Bag of Words, BoW). В качестве примера возьмем уже известный нам текст про Париж, музеи и искусство.
1 |
corpus = 'When we were in Paris we visited a lot of museums. We first went to the Louvre, the largest art museum in the world. I have always been interested in art so I spent many hours there. The museum is enourmous, so a week there would not be enough.' |
Предварительная обработка текста
Превратим строку в список слов.
1 2 |
words = corpus.split() print(words) |
1 |
['When', 'we', 'were', 'in', 'Paris', 'we', 'visited', 'a', 'lot', 'of', 'museums.', 'We', 'first', 'went', 'to', 'the', 'Louvre,', 'the', 'largest', 'art', 'museum', 'in', 'the', 'world.', 'I', 'have', 'always', 'been', 'interested', 'in', 'art', 'so', 'I', 'spent', 'many', 'hours', 'there.', 'The', 'museum', 'is', 'enourmous,', 'so', 'a', 'week', 'there', 'would', 'not', 'be', 'enough.'] |
Применим list comprehension, чтобы избавиться от точек и запятых. Помимо этого, переведем все слова в нижний регистр.
1 2 |
words = [word.strip('.').strip(',').lower() for word in words] print(words) |
1 |
['when', 'we', 'were', 'in', 'paris', 'we', 'visited', 'a', 'lot', 'of', 'museums', 'we', 'first', 'went', 'to', 'the', 'louvre', 'the', 'largest', 'art', 'museum', 'in', 'the', 'world', 'i', 'have', 'always', 'been', 'interested', 'in', 'art', 'so', 'i', 'spent', 'many', 'hours', 'there', 'the', 'museum', 'is', 'enourmous', 'so', 'a', 'week', 'there', 'would', 'not', 'be', 'enough'] |
Мы готовы создавать мешки слов разными способами.
Способ 1. Условие if-else
Начнем с простых условий if-else.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# создадим пустой словарь для мешка слов bow bow_1 = {} # пройдемся по словам текста for word in words: # если нам встретилось слово, которое уже есть в словаре if word in bow_1: # увеличим его значение (частоту) на 1 bow_1[word] = bow_1[word] + 1 # в противном случае, если слово встречается впервые else: # зададим ему значение 1 bow_1[word] = 1 # отсортируем словарь по значению в убываюем порядке (reverse = True) # и выведем шесть наиболее частотных слов sorted(bow_1.items(), key = lambda x : x[1], reverse = True)[:6] |
1 |
[('the', 4), ('we', 3), ('in', 3), ('a', 2), ('art', 2), ('museum', 2)] |
Способ 2. Метод .get()
Помимо этого мы можем использовать метод .get().
1 2 3 4 5 6 |
bow_2 = {} for word in words: bow_2[word] = bow_2.get(word, 0) + 1 sorted(bow_2.items(), key = lambda x : x[1], reverse = True)[:6] |
1 |
[('the', 4), ('we', 3), ('in', 3), ('a', 2), ('art', 2), ('museum', 2)] |
Поясню приведенный код. Метод .get() проверит наличие ключа (слова) в словаре и, если его нет, выведет 0.
1 |
bow_2.get(word, 0) |
Далее мы прибавим к нулю единицу (потому что слово встретилось первый раз) и запишем это слово в наш словарь.
1 |
bow_2[word] = bow_2.get(word, 0) + 1 |
Если такой ключ (слово) уже есть, метод .get() выведет его значение (частоту слова) и мы просто увеличим это значение на один.
Способ 3. Класс Counter модуля collections
Напомню, что этот же функционал реализован в классе Counter модуля collections.
1 2 3 4 5 6 7 8 |
# импортируем класс Counter from collections import Counter # создадим объект этого класса, передав ему список слов bow_3 = Counter(words) # выведем шесть наиболее часто встречающихся слов с помощью метода .most_common() bow_3.most_common(6) |
1 |
[('the', 4), ('we', 3), ('in', 3), ('a', 2), ('art', 2), ('museum', 2)] |
Результат. Обратите внимание, в отличие от мешка слов, который мы создавали на занятии по обработке естественного языка, в число наиболее популярных слов вошли артикли, предлоги и союзы. Они не несут полезной информации о содержании текста. Это так называемые стоп-слова. Кроме того, мы не провели лемматизацию, и поэтому museum и museums считаются разными словами.
На этом мы завершаем наше занятие, полученных знаний должно быть достаточно для дальнейшей работы.
Подведем итог
Сегодня мы подробно рассмотрели, что собой представляет словарь в Питоне.
Мы изучили структуры словаря, основные операции со словарями, использование dict comprehension, а также рассмотрели ряд дополнительных примеров. В частности, мы узнали про возможность создания словаря с помощью lambda-функции, а также функции map() и zip(), вложенные словари и несколько способов подсчета частоты слов в заданном тексте.
Вопросы для закрепления
Вопрос. Есть ли индекс у элементов словаря в Питоне?
Посмотреть правильный ответ
Ответ: нет, элементы словаря в Питоне не упорядочены, и у них отсутствует индекс. Доступ к элементам словаря осуществляется по ключу.
Вопрос. С помощью какого метода можно соединить два словаря в один?
Посмотреть правильный ответ
Ответ: для этого подойдет метод .update().
Вопрос. Почему при копировании объекта в Питоне лучше использовать метод .copy()?
Посмотреть правильный ответ
Ответ: если для копирования использовать оператор присваивания =, то при изменении нового объекта изменится и исходный. Это происходит потому, что новая переменная лишь ссылается на объект, содержащийся в исходной переменной.
В ноутбуке к лекции приведены дополнительные упражнения⧉.
В следующий раз мы поговорим про массив Numpy.
Ответы на вопросы
Вопрос. А что будет, если в словаре есть одинаковые ключи?
Ответ. Давайте посмотрим на примере. При создании словаря с одинаковыми ключами:
1 |
{'k1' : 1, 'k1' : 2, 'k1' : 3} |
Питон выведет:
1 |
{'k1': 3} |
Как мы видим, Питон оставил только последний элемент из серии одинаковых ключей. Ошибки при этом не возникает.
Вопрос. Словарь — это изменяемый или неизменяемый тип данных в Питоне?
Ответ. Большое спасибо за отличный вопрос. Давайте в целом разберем, какие типы данных в Питоне считаются изменяемыми (mutable), а какие неизменяемыми (immutable).
Весь код на Питоне представляет собой объекты либо отношения между ними. У каждого объекта есть identity (адрес объекта в памяти компьютера), type (тип) и value (значение).
Создадим строковый объект и посмотрим на identity, type и value.
1 2 3 4 5 6 |
# создадим объект string = 'Python' # посмотрим на identity, type и value # функция id() выводит адрес объекта в памяти компьютера id(string), type(string), string |
1 |
(140324781601584, str, 'Python') |
Теперь попробуем изменить этот объект.
1 2 3 4 5 |
# расширим наше представление о Питоне string = string + ' is cool' # посмотрим на identity, type и value id(string), type(string), string |
1 |
(140324510145712, str, 'Python is cool') |
Как вы видите, адрес объекта в памяти изменился, а значит это уже другой объект и тип str относится к неизменяемым типам данных.
Теперь возьмем список, выведем его identity, type и value, добавим в этот список новый элемент и посмотрим как изменятся его свойства.
1 2 3 4 5 |
# создадим список lst = [1, 2, 3] # посмотрим на identity, type и value id(lst), type(lst), lst |
1 |
(140324385431872, list, [1, 2, 3]) |
1 2 3 4 5 |
# добавим элемент в список lst.append(4) # снова выведем identity, type и value id(lst), type(lst), lst |
1 |
(140324385431872, list, [1, 2, 3, 4]) |
Список пополнился новым элементом, при этом оставшись в той же ячейке памяти. Это изменяемый тип данных.
- В целом к изменяемым типам данных относятся: список, словарь, множество и созданные пользователем классы
- Неизменяемые типы данных: int, float, bool, str, tuple и range.
Теперь посмотрим как свойство изменяемости и неизменяемости влияет на копирование объектов. Начнем со строки.
1 2 3 4 5 6 7 8 9 10 11 |
# вновь создадим строку string = 'Python' # скопируем через присваивание string2 = string # изменим копию string2 = string2 + ' is cool' # посмотрим на результат string, string2 |
1 |
('Python', 'Python is cool') |
Благодаря тому, что при изменении строки был создан новый объект, копирование прошло успешно (исходная строка при изменении копии не была затронута). Тот факт, что мы имеем дело с разными объектами можно также проверить через оператор is.
1 2 3 |
# оператор == сравнивает значения (values) # оператор is сравнивает identities string == string2, string is string2 |
1 |
(False, False) |
Посмотрим, что будет со списком.
1 2 3 4 5 6 7 8 9 10 11 |
# создадим список lst = [1, 2, 3] # скопируем его в новую переменную через присваивание lst2 = lst # добавим новый элемент в скопированный список lst2.append(4) # выведем исходный список и копию lst, lst2 |
1 |
([1, 2, 3, 4], [1, 2, 3, 4]) |
В данном случае копирование через присваивание и последующее добавление новых элементов в список не создало новых объектов. По этой причине изменился как исходный объект, так и его копия.
1 2 |
# убедимся, что речь идет об одном и том же объекте lst == lst2, lst is lst2 |
1 |
(True, True) |
Как и в случае со словарем, необходимо использовать метод .copy().
1 2 3 4 5 6 7 8 9 10 11 |
# вновь создадим список lst = [1, 2, 3] # скопируем с помощью метода .copy() lst2 = lst.copy() # добавим новый элемент в скопированный список lst2.append(4) # выведем исходный список и копию lst, lst2 |
1 |
([1, 2, 3], [1, 2, 3, 4]) |
Теперь копирование прошло успешно. Прежде чем завершить, давайте добавим число четыре в исходный список и убедимся, что речь идет о разных объектах.
1 2 3 |
lst.append(4) lst, lst2, lst == lst2, lst is lst2 |
1 |
([1, 2, 3, 4], [1, 2, 3, 4], True, False) |
Значения списков совпадают, а вот их адреса — нет.