3.2. Множества, словари

В этой статье мы продолжим изучать коллекции — но теперь познакомимся с неупорядоченными типами данных. Вы узнаете, как работают множества (set) и словари (dict), чем они отличаются от списков и в каких задачах используются. Научитесь проверять, содержится ли элемент в множестве, находить пересечения и разности, а также хранить и быстро получать значения по ключам. Мы разберёмся, как создавать множества и словари, выполнять над ними операции, использовать их методы и применять в реальных примерах.

Ключевые вопросы статьи

  • Чем множества отличаются от списков и зачем нужны?
  • Как устроено множество и какие операции с ним поддерживаются?
  • Что такое словарь и как обращаться к элементам по ключу?
  • Как добавлять и удалять элементы в словарях?
  • Какие методы доступны у множеств и словарей?

Множества (set)

Множество — это тип коллекции, в которой автоматически удаляются повторяющиеся значения, а порядок не сохраняется. У множеств нет индексов, но с ними можно выполнять полезные операции: быстро проверять наличие элемента, находить пересечения и объединения, удалять дубли. Начнём с того, как создавать множества и преобразовывать в них другие коллекции.

Создание множества и преобразование из других коллекций

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

vowels = {"а", "е", "ё", "и", "о", "у", "ы", "э", "ю", "я"}

Если нужно создать пустое множество, используют функцию set() без аргументов. А чтобы определить, сколько в нём элементов, подойдёт уже знакомая функция len():

empty_set = set()
print(f"Длина пустого множества равна {len(empty_set)}.")

Вывод программы:

Длина пустого множества равна 0.

Множество можно также получить из другой коллекции. Например, вот как преобразовать строку в множество — получится набор уникальных символов:

word = "коллекция"
letters = set(word)
print(letters)

При каждом запуске порядок элементов может отличаться:

{'е', 'о', 'и', 'я', 'к', 'л', 'ц'}
{'л', 'к', 'и', 'ц', 'я', 'е', 'о'}

Неупорядоченность и уникальность

У множеств есть две важные особенности:

  • Элементы не имеют фиксированного порядка. При каждом запуске программы порядок элементов может быть разным.
  • Элементы не повторяются. В множестве автоматически хранятся только уникальные значения — дубликаты удаляются.

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

Проверка принадлежности

Чтобы проверить, есть ли значение во множестве, используют оператор in. Например, определим, является ли введённая буква гласной:

vowels = {"а", "е", "ё", "и", "о", "у", "ы", "э", "ю", "я"}
letter = input("Введите букву русского алфавита: ")
if letter.lower() in vowels:
    print("Гласная буква")
else:
    print("Согласная буква")

Перебор элементов множества

Пройти по множеству можно в цикле for:

vowels = {"а", "е", "ё", "и", "о", "у", "ы", "э", "ю", "я"}
for letter in vowels:
    print(letter)

Поскольку порядок элементов не фиксирован, при каждом запуске результат может отличаться — и это нормально для множеств.

Операции над множествами

Множества в Python позволяют выполнять основные множественные операции, которые часто встречаются в задачах обработки данных.

Рассмотрим каждую из них.

  • Объединяет два множества — результат содержит все элементы из обоих. Повторы исключаются. Оператор: | или метод union().

Пример:

s_1 = {1, 2, 3}
s_2 = {3, 4, 5}
s_union = s_1 | s_2
# s_union = s_1.union(s_2)
print(s_union)

Вывод программы:

{1, 2, 3, 4, 5}
  • Возвращает только те элементы, которые есть в обоих множествах. Оператор: & или метод intersection().

Пример:

s_1 = {1, 2, 3}
s_2 = {3, 4, 5}
s_intersection = s_1 & s_2
# s_intersection = s_1.intersection(s_2)
print(s_intersection)

Вывод программы:

{3}
  • Возвращает элементы из первого множества, которых нет во втором. Оператор: - или метод difference().

Пример:

s_1 = {1, 2, 3}
s_2 = {3, 4, 5}
s_dif = s_1 - s_2
# s_dif = s_1.difference(s_2)
print(s_dif)

Вывод программы:

{1, 2}
  • Возвращает элементы, которые есть только в одном из двух множеств (в первом или втором, но не в обоих). Оператор: ^ или метод symmetric_difference().

Пример:

s_1 = {1, 2, 3}
s_2 = {3, 4, 5}
s_sym_dif = s_1 ^ s_2
# s_sym_dif = s_1.symmetric_difference(s_2)
print(s_sym_dif)

Вывод программы:

{1, 2, 4, 5}

Пример: найти гласные в слове

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

vowels = {"а", "е", "ё", "и", "о", "у", "ы", "э", "ю", "я"}
letters = set("коллекция")
print(", ".join(letters & vowels))

Вывод программы:

о, я, и, е

Сравнение множеств

В Python можно сравнивать множества — это удобно для анализа подмножеств, совпадений и включений. Вот основные операции:

  • Проверяет, содержат ли два множества одни и те же элементы, независимо от порядка. Оператор: ==.
s_1 = {1, 2, 3}
s_2 = {3, 1, 2}
print(s_1 == s_2)

Вывод программы:

True
  • Проверяет, входят ли все элементы одного множества в другое. Оператор: <=.
s_1 = {1, 2, 3}
s_2 = {1, 2, 3, 4}
print(s_1 <= s_2)

Вывод программы:

True

Проверяет, содержит ли множество все элементы другого множества. Оператор: >=.

s_1 = {1, 2, 3, 4}
s_2 = {1, 2, 3}
print(s_1 >= s_2)

Вывод программы:

True

Методы, изменяющие множество

Множества — изменяемый тип данных. В следующей таблице приведены методы, которые позволяют добавлять, удалять и очищать элементы. Они изменяют множество напрямую.

set.add(e)

Описание
Добавить элемент во множество

Пример:

s = set()
s.add(1)
print(s)

Вывод:

{1}
set.remove(e)

Описание
Удалить элемент множества. Возвращает исключение KeyError, если элемент не принадлежит множеству

Пример:

s = {1, 2, 3}
s.remove(2)
print(s)

Результат:

{1, 3}
set.discard(e)

Описание
Удалить элемент, если он принадлежит множеству

Пример:

s = {1, 2, 3}
s.discard(2)
print(s)

Вывод:

{1, 3}
set.pop()

Описание
Вернуть и удалить произвольный элемент множества

Пример:

s = {1, 2, 3}
x = s.pop()
print(x)
print(s)

Вывод:

2
{1, 3}
set.clear()

Описание
Очистить множество, удалив все его элементы

Пример:

s = {1, 2, 3}
s.clear()
print(s)

Вывод:

set()

Словари (dict)

До этого мы работали с коллекциями, где данные упорядочены и к ним можно обращаться по индексу. Но что, если нужно находить значение не по номеру, а, например, по названию?

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

Когда списков недостаточно

Допустим, нам нужно сохранить информацию о странах и их столицах. Один из способов — использовать список кортежей, где каждая пара содержит страну и её столицу:

countries_and_capitals = [("Россия", "Москва"), ("США", "Вашингтон"), ("Франция", "Париж")]

Если мы хотим узнать столицу Франции, придётся пройтись по списку и найти нужную строку:

countries_and_capitals = [("Россия", "Москва"), ("США", "Вашингтон"), ("Франция", "Париж")]
for country in countries_and_capitals:
    if country[0] == "Франция":
      print(country[1])
      break

Этот подход работает, но он не самый удобный — особенно при большом объёме данных.

Как устроен словарь

Словарь (dict) — это коллекция пар ключ: значение. Ключ может быть, например, названием страны, а значение — её столицей.

countries_and_capitals = {"Россия": "Москва",
"США": "Вашингтон",
"Франция": "Париж"}
print(countries_and_capitals["Франция"])

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

Добавление и изменение значений

Чтобы взять значение по ключу, необходимо указать этот ключ в квадратных скобках после имени словаря:

Если же нужно добавить новый ключ в словарь, то его указывают после имени словаря в левой части операции присваивания, а значение, которое будет храниться по этому ключу, — в правой части:

countries_and_capitals = {"Россия": "Москва",
                          "США": "Вашингтон",
                          "Франция": "Париж"}
countries_and_capitals["Сербия"] = "Белград"
print(countries_and_capitals)

Примечание

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

d = {"key": "old_value"}
d["key"] = "new_value"
print(d["key"])

Вывод программы:

new_value

Обработка ошибок и проверка ключа

Если попытаться получить значение по несуществующему ключу, программа завершится с ошибкой KeyError:

countries_and_capitals = {"Россия": "Москва",
                          "США": "Вашингтон",
                          "Франция": "Париж"}
print(countries_and_capitals["Сербия"])

Вывод программы:

KeyError: 'Сербия'

Чтобы избежать этой ошибки, перед обращением к значению словаря стоит проверить, существует ли ключ. Для этого используется уже знакомый нам оператор in:

countries_and_capitals = {"Россия": "Москва",
                          "США": "Вашингтон",
                          "Франция": "Париж"}
if "Сербия" in countries_and_capitals:
    print(countries_and_capitals["Сербия"])
else:
    print("Страна пока не добавлена в словарь")

Перебор словаря в цикле

Пройтись по всем ключам словаря можно с помощью цикла for. При этом на каждой итерации переменная цикла принимает ключ, а значение можно получить по этому ключу:

countries_and_capitals = {"Россия": "Москва",
"США": "Вашингтон",
"Франция": "Париж"}
for country in countries_and_capitals:
    print(f"У страны {country} столица — {countries_and_capitals[country]}.")

Вывод программы:

У страны Россия столица — Москва.
У страны США столица — Вашингтон.
У страны Франция столица — Париж.

Значения-коллекции: пример с индексами строк

Значением в словаре может быть не только число или строка, но и коллекция — например, список. Это позволяет хранить по ключу сразу несколько связанных значений.

Рассмотрим пример: пользователь вводит названия стран построчно. Некоторые страны могут повторяться. Нужно сохранить, в каких строках (начиная с нулевой) встречалась каждая страна. Для этого создадим словарь, где ключ — это название страны, а значение — список номеров строк:

# создаём пустой словарь
countries = dict()
# вводим первую строку до цикла (можно заменить, использовав оператор-морж)
country = input()
# создаём счётчик номеров строк
str_number = 0
# продолжаем цикл, пока не введена строка «СТОП»
while country != "СТОП":
    # если введённой страны нет в словаре, создаём ключ и записываем по ключу список из одного номера строки
    if country not in countries:
        countries[country] = [str_number]
    # иначе добавляем в список по ключу новое значение номера строки
    else:
        countries[country].append(str_number)
    # увеличиваем счётчик
    str_number += 1
    # вводим следующую строку
    country = input()
# выводим название страны и полученные списки с новой строки
for country in countries:
    print(f"{country}: {countries[country]}")

Пример ввода:

США
США
Россия
Россия
Россия
Франция
Сербия
СТОП

Вывод программы:

США: [0, 1]
Россия: [2, 3, 4]
Франция: [5]
Сербия: [6]

Методы словаря

Словари поддерживают множество операций: добавление и удаление элементов, получение значений с запасным вариантом, перебор ключей и значений. Некоторые методы изменяют словарь, другие — только возвращают данные.

В следующей таблице перечислены основные операции и методы, которые чаще всего используются при работе со словарями. Они пригодятся как в учебных задачах, так и в реальных проектах.

len(d)

Описание
Возвращает количество ключей в словаре

Пример:

d = {"a": 1, "b": 2, "c": 3}
print(len(d))

Вывод:

3
del d[key]

Описание
Удалить ключ из словаря. Если ключа нет, то вызывается исключение KeyError

Пример:

d = {"a": 1, "b": 2, "c": 3}
del d["b"]
print(d)

Вывод:

{'a': 1, 'c': 3}
dict.clear()

Описание
Удалить все ключи и значения в словаре

Пример:

d = {"a": 1, "b": 2, "c": 3}
d.clear()
print(d)

Вывод:

{}
dict.get(key, default)

Описание
Возвращает значение по ключу key. Если ключа нет, то возвращает значение default

Пример:

d = {"a": 1, "b": 2, "c": 3}
print(d.get("e", "Ключа нет в словаре"))

Вывод:

Ключа нет в словаре
dict.items()

Описание
Возвращает итерируемый объект, состоящий из кортежей (ключ, значение) словаря

Пример:

d = {"a": 1, "b": 2, "c": 3}
for key, value in d.items():
    print(key, value)

Вывод:

a 1
b 2
c 3
dict.keys()

Описание
Возвращает итерируемый объект, состоящий из ключей словаря

Пример:

d = {"a": 1, "b": 2, "c": 3}
for key in d.keys():
    print(key)

Вывод:

a
b
c
d.pop(key, default)

Описание
Возвращает значение по ключу key и удаляет его из словаря. Если ключа нет, то возвращает default

Пример:

d = {"a": 1, "b": 2, "c": 3}
x = d.pop("a")
print(x)

Вывод:

1
dict.values()

Описание
Возвращает итерируемый объект, состоящий из значений словаря

Пример:

d = {"a": 1, "b": 2, "c": 3}
for value in d.values():
    print(value)

Вывод:

1
2
3

Тот же пример с использованием get()

Ранее мы реализовали программу, которая сохраняет номера строк, в которых встречалась каждая страна. Теперь перепишем её с использованием метода get(). Это позволит сократить код без потери логики.

# создаём пустой словарь
countries = dict()
# вводим первую строку до цикла (можно заменить, использовав оператор-морж)
country = input()
# создаём счётчик номеров строк
str_number = 0
# продолжаем цикл, пока не введена строка «СТОП»
while country != "СТОП":
    # Если страна country есть среди ключей, то get() возвращает список, 
    # хранящийся по этому ключу, иначе get() возвращает пустой список. 
    # Добавляем в список значение str_number.
    countries[country] = countries.get(country, []) + [str_number]
    # увеличиваем счётчик
    str_number += 1
    # вводим следующую строку
    country = input()
# выводим название страны и полученные списки с новой строки
for country in countries:
    print(f"{country}: {countries[country]}")

Метод get() позволил заменить четыре строчки кода одной. Попробуйте запустить оба варианта программы — вывод будет одинаковым.

Что дальше

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

Дальше мы сделаем ещё один шаг вперёд: разберёмся, как быстро создавать и фильтровать списки с помощью списочных выражений, узнаем, что такое генераторы и итераторы, а заодно заглянем «под капот» — и поймём, как Python хранит переменные в памяти.

В следующей статье:

  • вы познакомитесь со списочными выражениями (и узнаете, почему они читаются проще, чем for + append);
  • научитесь использовать вложенные циклы и условия прямо внутри списков;
  • узнаете, как устроены генераторы и почему они экономят память;
  • и, наконец, разберётесь, почему у переменных может меняться идентификатор, и что это вообще значит.

Ключевые выводы статьи

  • Множество (set) — неупорядоченная коллекция без повторов; используется для хранения уникальных значений и проверки принадлежности.
  • Множества поддерживают операции: объединение, пересечение, разность и симметричная разность.
  • Словарь (dict) хранит значения по ключам; ключи должны быть неизменяемыми типами, значения — любыми.
  • Для безопасного получения значений из словаря используют метод get() и проверку ключей через in.
  • В словарях можно хранить вложенные коллекции и перебирать данные по ключам, значениям или парам.