Словари

⏱ 35 мин · 🧩 заданий: 6

Словарь

Словарь (dict) хранит пары ключ → значение и находит значение по ключу в среднем за O(1) — независимо от размера.

user = {"name": "Анна", "age": 28, "city": "Москва"}
print(user["name"])
user["age"] = 29              # изменить
user["email"] = "a@mail.ru"   # добавить
print(user, len(user))
print("city" in user, "phone" in user)    # проверка ключа

Другие способы создать словарь:

a = dict(name="Анна", age=28)                 # именованные аргументы
b = dict([("x", 1), ("y", 2)])                 # из пар
c = dict(zip(["a", "b", "c"], [1, 2, 3]))      # из двух списков
d = {n: n * n for n in range(5)}               # словарное включение
e = dict.fromkeys(["read", "write"], False)    # одинаковые значения
print(a, b, c, d, e, sep="\n")

Безопасный доступ

config = {"debug": True}
print(config.get("debug"))
print(config.get("timeout"))          # None
print(config.get("timeout", 30))      # значение по умолчанию
try:
    config["timeout"]
except KeyError as e:
    print("KeyError:", e)

Изменение и удаление

d = {"a": 1, "b": 2, "c": 3}
d.update({"b": 20, "d": 4})          # обновить несколько
print(d)
print(d.pop("a"), d.pop("zzz", "нет такого"))   # удалить и вернуть
del d["c"]
print(d.popitem())                   # удалить последнюю добавленную пару
print(d)

merged = {"a": 1} | {"b": 2}         # Python 3.9+: объединение
merged |= {"a": 100}                 # обновить на месте
print(merged, {**merged, "c": 3})    # и старый способ — распаковка

setdefault: значение по умолчанию с добавлением

index = {}
for word in ["кот", "кит", "пёс", "паук"]:
    index.setdefault(word[0], []).append(word)   # если ключа нет — создать []
print(index)

setdefault(k, default) возвращает значение по ключу, а если ключа нет — вставляет default и возвращает его. Ещё удобнее — collections.defaultdict (следующие уроки).

Перебор

prices = {"кофе": 199, "чай": 99, "сок": 150}
for key in prices:                    # по ключам
    print(key, end=" ")
print()
for name, price in prices.items():    # пары
    print(f"{name}: {price}")
print(list(prices.keys()), list(prices.values()), sum(prices.values()))
print(max(prices, key=prices.get))    # ключ с максимальным значением

keys(), values(), items() возвращают представления (views): они не копируют данные и отражают изменения словаря. keys() и items() поддерживают операции множеств:

a = {"x": 1, "y": 2}
b = {"y": 3, "z": 4}
print(a.keys() & b.keys(), a.keys() - b.keys(), a.keys() | b.keys())

Не меняйте размер словаря во время перебора

for k in d: del d[k] → RuntimeError: dictionary changed size during iteration. Перебирайте копию ключей: for k in list(d):.

Порядок

С Python 3.7 словарь гарантированно сохраняет порядок вставки. Изменение значения существующего ключа порядок не меняет; удаление и повторная вставка — переносит ключ в конец.

d = {"b": 1, "a": 2}
d["c"] = 3
d["b"] = 10
print(list(d))
print(dict(sorted(d.items())))                       # отсортировать по ключу
print(dict(sorted(d.items(), key=lambda kv: kv[1]))) # по значению

Ключи

Ключ должен быть хешируемым (неизменяемым): числа, строки, кортежи из хешируемых, frozenset. Значения — любые.

d = {}
d[(1, 2)] = "кортеж — можно"
print(d)
d[[1, 2]] = "список — нельзя"     # TypeError: unhashable type: 'list'

1 и True — один ключ

{1: "a", True: "b"} даёт {1: "b"}: 1 == True и hash(1) == hash(True), поэтому это один и тот же ключ. Любимый вопрос «что выведет код».

Вложенные словари и JSON

company = {
    "name": "Рога и копыта",
    "employees": [
        {"name": "Анна", "skills": ["Python", "SQL"]},
        {"name": "Борис", "skills": ["Go"]},
    ],
}
print(company["employees"][0]["skills"][1])
for emp in company["employees"]:
    print(emp["name"], ", ".join(emp["skills"]))

import json
print(json.dumps(company, ensure_ascii=False, indent=2))

Словари и списки один в один соответствуют JSON — поэтому в вебе с ними работают постоянно.

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

Внутри — хеш-таблица. Для ключа вычисляется hash(key), по нему находится ячейка. Поэтому поиск, вставка и удаление — O(1) в среднем (в худшем случае при множестве коллизий — O(n)). Подробно разберём в уроке «Хеширование: как устроены dict и set».

Операция Сложность
d[k], d[k] = v, k in d, del d[k], get O(1) в среднем
перебор, copy(), values() в список O(n)

Итог

  • dict — ключ → значение, поиск O(1) в среднем; порядок вставки сохраняется.
  • d[k] бросает KeyError, d.get(k, default) — нет; setdefault вставляет значение по умолчанию.
  • Перебор: for k, v in d.items(); не меняйте размер во время перебора.
  • Объединение: a | b, {**a, **b}, update.
  • Ключи — только хешируемые объекты.

Практика решено 0 из 6

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Программа на Python

Напишите функцию word_count(text), которая возвращает словарь «слово → сколько раз встретилось». Слова разделены пробелами, регистр не учитывать. Не используйте collections.Counter.

Какие тесты проверяют код (3)
assert word_count("кот пёс Кот") == {"кот": 2, "пёс": 1}
assert word_count("") == {}
assert word_count("a a a") == {"a": 3}
Задание 2 Один вариант ответа

Чем d["x"] отличается от d.get("x"), если ключа нет?

Задание 3 Программа на Python

Напишите функцию group_by_length(words), которая группирует слова по длине: возвращает словарь «длина → список слов в исходном порядке».

Какие тесты проверяют код (2)
assert group_by_length(["кот", "пёс", "слон", "ёж"]) == {3: ["кот", "пёс"], 4: ["слон"], 2: ["ёж"]}
assert group_by_length([]) == {}
Задание 4 Программа на Python

Напишите функцию invert(d), которая меняет местами ключи и значения. Если несколько ключей имеют одинаковое значение, в результате должен остаться последний из них.

Какие тесты проверяют код (3)
assert invert({"a": 1, "b": 2}) == {1: "a", 2: "b"}
assert invert({"a": 1, "b": 1}) == {1: "b"}
assert invert({}) == {}
Задание 5 Несколько вариантов

Что можно использовать в качестве ключа словаря?

Задание 6 Один вариант ответа

Сохраняет ли обычный dict порядок добавления ключей?

Войдите, чтобы отмечать пройденные уроки.