📦 Репозиторий занятия 36

Урок 36. Словари. Продолжение

Как работать с репозиторием

Каждая карточка говорит, о чём файл, что он выводит и что в нём искать. Код виден прямо здесь: его можно скопировать одной кнопкой, скачать файл или открыть его целиком.

Маршрут изучения

  1. Прочитайте описание: по нему уже понятно, о чём файл и что он выведет.
  2. Предскажите вывод: сравните своё предположение со строкой «Что выводит».
  3. Запустите: скачайте файл или скопируйте код кнопкой и выполните его у себя.
  4. Измените: поменяйте одно условие или значение и объясните новый результат.

Файлы: рекомендуемый порядок

1
MarkdownРазбор концепции143 строк

Семь способов создать словарь

less_19__dict/theory_01__dict_creation.md

Систематизирует все способы создания словаря: буквальный синтаксис {}, функция dict() (с kwargs, списком пар, копированием, zip()), dict comprehension, dict.fromkeys(), unpacking **, разбор JSON-строки и collections.defaultdict. Завершается сводной таблицей «когда что использовать» — удобный чеклист для выбора подходящего способа под конкретную задачу.

  • {'a': 1} — литеральный синтаксис, ключи только hashable
  • dict(a=1, b=2) — через kwargs, только если ключ — валидное имя переменной
  • dict([('a',1)]) и dict(zip(keys, values)) — из пар и из двух списков
  • {x: x**2 for x in range(10) if x % 2 == 0} — dict comprehension
  • dict.fromkeys(['a','b','c'], 0) — один и тот же default для всех ключей
  • {**a, 'z': 3} — unpacking с объединением словарей
  • json.loads(...) и defaultdict(int)/defaultdict(list) — из JSON и с фабрикой значений
Показать начало файла (143 строк всего)
# Cпособы создания словаря

## 1. Через фигурные скобки `{}`

```python
d = {'a': 1, 'b': 2, 'c': 3}

d_empty = {}  # пример пустого словаря
```

* Ключи должны быть `immutable` (`hashable`)

---

## 2. С помощью функции `dict()`

Функция `dict()` может принимать разные виды аргументов.

```python
d_empty = dict() # пример пустого словаря
```

### 2.1. С именованными аргументами (`kwargs`)

```python
d = dict(a=1, b=2, c=3)  # {'a': 1, 'b': 2, 'c': 3}
```

⚠️ Работает только если ключи — корректные имена переменных (строки без пробелов и спецсимволов).

---

### 2.2. Из двумерного массива

```python
d = dict([('a', 1), ('b', 2), ('c', 3)])
```

* Можно использовать **любую итерируемую структуру**, где каждый элемент — пара `(ключ, значение)`.
* Первый элемент может ТОЛЬКО `immutable` (`hashable`).

---

### 2.3. Из другого словаря

```python
old = {'a': 1, 'b': 2}
new = dict(old)
```

Создаётся поверхностная **копия** исходного словаря.

---

### 2.4. Из `zip()` с двумя аргументами (коллекциями)

```python
keys = ['a', 'b', 'c']
values = [1, 2, 3]
d = dict(zip(keys, values))
…
Проверьте себя: Почему dict(a=1, b=2) не сработает, если ключ, например, '2a' или содержит пробел, и какой способ создания словаря использовать вместо него?
2
MarkdownРазбор концепции180 строк

.get() vs .setdefault(): читать безопасно или читать-и-создавать

less_19__dict/theory_02__get__vs__setdefault.md

Сравнивает два метода на конкретных примерах: подсчёт частоты символов в строке и группировка слов по первой букве. Показывает, что get() не меняет словарь, а setdefault() создаёт пару при отсутствии ключа — и что для mutable значений (списков) именно setdefault() короче и без лишних обращений к словарю. Итоговое правило: mutable-значения → setdefault(), immutable-значения → get().

  • d.get(key, default) — читает, не создаёт запись, словарь не меняется
  • d.setdefault(key, default) — читает или создаёт запись со значением default
  • Задача 1: подсчёт частоты символов текста через d.get(char, 0) + 1
  • Задача 2: список повторов через d.setdefault(char, []).append(1)
  • Задача 3 (практическая): группировка слов по первой букве через setdefault
  • Почему d.get(key, []).append(word) нельзя сократить — append() возвращает None
Показать начало файла (180 строк всего)
# Сравнение методов `.get()` и `.setdefault()`

## 1. Вспоминаем эти методы

### 1.1. Метод `dict.get()`

```python
dict.get(key, default=None)
```

* Возвращает значение по ключу, если ключ есть.
* Если ключа **нет**, возвращает `default` (по умолчанию `None`).
* ⚠️ **Не изменяет** сам словарь.

```python
d = {'a': 1, 'b': 2}

print(d.get('a'))        # 1
print(d.get('c'))        # None
print(d.get('c', 0))     # 0

print(d)  # {'a': 1, 'b': 2} — словарь не изменился
```

---

## 1.2. Метод `dict.setdefault()`

```python
dict.setdefault(key, default=None)
```

* Если ключ **существует**, возвращает его значение.
* Если ключа **нет**, создаёт новую пару `key: default` в словаре.
* ⚠️ **Изменяет** словарь, если ключа ещё не было.

```python
d = {'a': 1, 'b': 2}

print(d.setdefault('a'))        # 1 (уже есть)
print(d.setdefault('c'))        # None (создался со значением None)
print(d.setdefault('d', 0))     # 0 (создался со значением 0)

print(d)
# {'a': 1, 'b': 2, 'c': None, 'd': 0}
```

---

## 2. Ключевые различия

| Особенность                                 | `get()`                         | `setdefault()`                         |
| ------------------------------------------- |---------------------------------|----------------------------------------|
| Возвращает значение по ключу                | ✅ Да                           | ✅ Да                                   |
| Если ключ отсутствует — добавляет в словарь | ❌ Нет                          | ✅ Да                                   |
| Изменяет словарь                            | ❌ Нет                          | ✅ Да (если ключа не было)              |
| Значение по умолчанию                       | Возвращает, **но не сохраняет** | Возвращает и **сохраняет**             |
| Типичный сценарий                           | Безопасно читать                | Читать **и при необходимости создать** |

---
…
Проверьте себя: В задаче 3 вариант с get() делает d[key] = d.get(key, []) отдельной строкой, а не сразу .append() — почему такая запись обязательна, в отличие от однострочного варианта с setdefault()?
3
MarkdownРазбор концепции61 строк

dict.fromkeys() и ловушка общего изменяемого значения

less_19__dict/theory_03__fromkeys.md

Разбирает метод dict.fromkeys(iterable, value) для создания словаря с одинаковым значением у всех ключей. Главный акцент — предупреждение: если value — изменяемый объект (список, словарь), все ключи ссылаются на ОДИН и тот же объект в памяти, и изменение через один ключ становится видно у всех остальных ключей сразу.

  • dict.fromkeys(keys) — все значения None по умолчанию
  • dict.fromkeys(keys, 0) — одинаковое immutable-значение у всех ключей
  • dict.fromkeys(keys, []) — ловушка: все ключи ссылаются на один и тот же список
  • d['a'].append(1) меняет значения сразу у всех ключей
  • Тот же эффект с {} — общий словарь у ключей 'low'/'middle'/'high'
Показать начало файла (61 строк всего)
## Метод `dict.fromkeys()`

Удобный способ создать новый словарь, где все указанные ключи получают одно и то же значение (по умолчанию — `None`).

```python
dict.fromkeys(iterable, value=None)
```

* `iterable` — итерируемый объект (список, строка, кортеж и т.п.), содержащий ключи.
* `value` — (необязательный) — значение, которое получат все ключи (по умолчанию `None`).

### Без значения

```python
keys = ['a', 'b', 'c']
d = dict.fromkeys(keys)
print(d)
# {'a': None, 'b': None, 'c': None}

```


### Указание значения по умолчанию

```python
keys = ['a', 'b', 'c']
d = dict.fromkeys(keys, 0)
print(d)
# {'a': 0, 'b': 0, 'c': 0}
```

---

## ⚠️ Важно: если значение — **изменяемый объект**

Если передать изменяемый объект (например, список, словарь, множество) как `value`,
**все ключи будут ссылаться на один и тот же объект!**

### Пример1:

```python
keys = ['a', 'b', 'c']
d = dict.fromkeys(keys, [])
d['a'].append(1)
print(d)
# {'a': [1], 'b': [1], 'c': [1]}  ❗ все три ссылаются на один и тот же список!
```

### Пример2:


```python
keys = ['low', 'middle', 'high']
d = dict.fromkeys(keys, {})
d['low']['milk'] = 5
d['high']['diamond'] = 10000

print(d)
# {'low': {'milk': 5, 'diamond': 10000}, 'middle': {'milk': 5, 'diamond': 10000}, 'high': {'milk': 5, 'diamond': 10000}}

…
Проверьте себя: Как правильно создать словарь, где у каждого ключа СВОЙ независимый пустой список, если dict.fromkeys(keys, []) для этого не подходит?
4
MarkdownРазбор концепции71 строк

Сравнение словарей, оператор | и операции над ключами как множествами

less_19__dict/theory_04__dict_comparison.md

Показывает, что словари сравниваются через == как множества пар ключ-значение — порядок ключей не важен, важны только сами пары и типы значений. Отдельно разбирает оператор | (с версии 3.9) для объединения словарей и то, что d.keys() возвращает view-объект, поддерживающий все операции множеств: &, -, ^, |.

  • d1 == d2 — сравнение по ключам и значениям, без учёта порядка добавления
  • d1 == d3 — False из-за разницы типов значений (1 != '1')
  • d1 | d2 — объединение словарей (Python 3.9+), правое значение побеждает при совпадении ключей
  • d1.keys() & d2.keys() — пересечение ключей
  • d1.keys() - d2.keys() и d1.keys() ^ d2.keys() — разность и симметрическая разность ключей
Показать начало файла (71 строк всего)
## Cравнение словарей

### 1. Полное сравнение словарей

#### 1.1. Равенство `==`

```python
d1 = {"a": 1, "b": 2}
d2 = {"b": 2, "a": 1}
d3 = {"b": 2, "a": "1"}

print(d1 == d2)  # True
print(d1 == d3)  # False - разница в типе value: 1 != "1"
```

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

---

#### 1.1. Неравенство `!=`

```
print(d1 != d3)  # True
```

---

# Что учитывается

| Элемент        | Учитывается |
| -------------- | ----------- |
| Ключи          | ✔           |
| Значения       | ✔           |
| Порядок ключей | ❌           |

---

### 2. Операции со словарями

С версии 3.9 возможно ТОЛЬКО объединение словарей

```python
d1 = {"a": 1, "b": 2}
d2 = {"b": 3, "c": 5}

d = d1 | d2
print(d)  # {'a': 1, 'b': 3, 'c': 5}
```

Если ключи совпадают, то значение справа заменяет значение слева.

---

### 3. Сравнение ключей

Ключи (но не словари!) поддерживают все операции множеств

```python"
d1.keys() == d2.keys()

…
Проверьте себя: Почему d1.keys() & d2.keys() работает как операция над множествами, а сами словари d1 & d2 так напрямую сравнить нельзя?