Основы программирования

Объекты и словари

**Загадка:** У тебя есть данные пользователя: имя, возраст, email, город. В списке это `["Алиса", 25, "alice@mail.com", "Москва"]`. Что означает `user[2]`? А если добавить телефон - все индексы сломаются! Есть решение лучше.

Словарь (dict) хранит данные по ключам, а не по индексам. Вместо `user[2]` пишешь `user["email"]` - понятно и надёжно.

Цели урока

  • Понять концепцию словаря (dict)
  • Освоить создание, чтение, изменение
  • Научиться итерировать по словарям
  • Познакомиться с вложенными структурами

Предварительные знания

  • Списки (урок 11)
  • Циклы (урок 7)
  • Строки (урок 5)

JSON - формат данных интернета - это словари! API возвращают словари. Конфиги - словари. Базы данных - записи-словари. Это самая важная структура данных.

  • **API ответы**: {"status": "ok", "data": [...]}
  • **Конфигурации**: {"debug": true, "port": 3000}
  • **Профили**: {"name": "Алиса", "age": 25}
  • **Кэширование**: {"url": "cached_response"}

От Sketchpad и Simula 67 до Smalltalk, Python everything-is-object и dataclasses

Идея объекта как сущности с данными и поведением появилась в 1963 году в Sketchpad Ивана Сазерленда (MIT, докторская в Lincoln Lab). Объекты там были графическими примитивами с прототипами. В 1967 году Кристен Нюгор и Уле-Йохан Даль в Норвежском вычислительном центре выпустили Simula 67, первый язык с классами, наследованием и виртуальными методами. От Simula напрямую произошёл C++ (Бьёрн Страуструп начал в 1979, релиз 1983 как C with Classes). В Xerox PARC Алан Кэй и команда (Дэн Ингаллс, Адель Голдберг) с 1972 по 1980 строили Smalltalk: всё было объектом, общение через сообщения, динамическая типизация. Smalltalk-80 дал миру MVC, рефакторинг и IDE. Java от Джеймса Гослинга (Sun, 1995) взяла модель классов Simula и виртуальную машину похожую на Smalltalk. В том же 1995 Брендан Эйх создал JavaScript за 10 дней, выбрал прототипное наследование вместо классов (классы как синтаксис добавили в ES6 2015). Python с 1991 года реализовал философию everything is an object: число, функция, класс, модуль это объекты. type(x) работает для любого значения. Словарь стал главной структурой данных, dict реализует и сам namespace модуля, и атрибуты экземпляра (__dict__). В Python 3.7 (2017, PEP 468) dict гарантирует порядок вставки, а PEP 557 (Eric V. Smith, Python 3.7, 2018) принёс dataclasses: декоратор @dataclass генерирует __init__, __repr__ и __eq__ без бойлерплейта.

Создание и основы

**Словарь (dict)** - коллекция пар "ключ: значение". Ключ - уникальный идентификатор, значение - любые данные.

Создание словарей

Разные способы

```python # Литерал - фигурные скобки user = { "name": "Алиса", "age": 25, "email": "alice@mail.com" } # Пустой словарь empty = {} empty2 = dict() # Из списка пар pairs = [("a", 1), ("b", 2)] d = dict(pairs) # {"a": 1, "b": 2} # Kwargs синтаксис (только строковые ключи) person = dict(name="Боб", age=30) ```

Ключи и значения

Что может быть ключом?

```python # Ключи - неизменяемые типы: str, int, tuple data = { "name": "Алиса", # Строка - ключ 42: "ответ", # Число - ключ (0, 0): "начало", # Кортеж - ключ } # НЕЛЬЗЯ: список как ключ (изменяемый) # bad = {[1, 2]: "value"} # TypeError! # Значения - любые типы complex_dict = { "list": [1, 2, 3], "nested": {"a": 1}, "func": print } ```

**Ключи уникальны!** Если добавить ключ, который уже существует - старое значение будет перезаписано.

Что выведет: len({"a": 1, "b": 2, "a": 3})?

Доступ и изменение

Доступ к значениям - через квадратные скобки с ключом или метод `.get()`.

Чтение значений

Два способа

```python user = {"name": "Алиса", "age": 25} # Способ 1: квадратные скобки print(user["name"]) # "Алиса" # print(user["email"]) # KeyError! Ключа нет # Способ 2: .get() - безопасный print(user.get("name")) # "Алиса" print(user.get("email")) # None (нет ошибки) print(user.get("email", "не указан")) # "не указан" (default) ```

Изменение и добавление

Словари изменяемы

```python user = {"name": "Алиса", "age": 25} # Изменение существующего user["age"] = 26 print(user) # {'name': 'Алиса', 'age': 26} # Добавление нового user["email"] = "alice@mail.com" print(user) # {'name': 'Алиса', 'age': 26, 'email': 'alice@mail.com'} # Удаление del user["age"] print(user) # {'name': 'Алиса', 'email': 'alice@mail.com'} # Проверка наличия ключа print("name" in user) # True print("age" in user) # False (удалили) ```

d[key] и d.get(key) - одно и то же

d[key] вызовет KeyError, если ключа нет. d.get(key) вернёт None.

Используй d[key], когда уверен, что ключ есть. Используй d.get(key, default) для безопасного доступа с fallback значением.

Что вернёт: {"a": 1}.get("b", 0)?

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

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

МетодОписаниеПример
.keys()Все ключиd.keys() → dict_keys(['a', 'b'])
.values()Все значенияd.values() → dict_values([1, 2])
.items()Пары (ключ, значение)d.items() → [('a', 1), ('b', 2)]
.pop(key)Удалить и вернутьd.pop('a') → 1
.update(d2)Объединить словариd.update({'c': 3})
.setdefault(k,v)Получить или установитьd.setdefault('x', 0)
.clear()Очиститьd.clear() → {}

Методы в действии

Практические примеры

```python scores = {"Alice": 85, "Bob": 92, "Charlie": 78} # Получить все данные print(list(scores.keys())) # ['Alice', 'Bob', 'Charlie'] print(list(scores.values())) # [85, 92, 78] print(list(scores.items())) # [('Alice', 85), ('Bob', 92), ...] # Удаление с получением bob_score = scores.pop("Bob") print(bob_score) # 92 print(scores) # {'Alice': 85, 'Charlie': 78} # Объединение scores.update({"Diana": 95, "Alice": 88}) # Alice перезаписан print(scores) # {'Alice': 88, 'Charlie': 78, 'Diana': 95} # setdefault - если нет, установить scores.setdefault("Eve", 0) # Добавит Eve: 0 scores.setdefault("Alice", 0) # НЕ изменит Alice (уже есть) ```

Что вернёт: {"a": 1, "b": 2}.pop("a")?

Итерация по словарям

Словари можно перебирать разными способами: по ключам, значениям или парам.

Способы итерации

Три варианта

```python user = {"name": "Алиса", "age": 25, "city": "Москва"} # По ключам (по умолчанию) for key in user: print(key) # name, age, city # По значениям for value in user.values(): print(value) # Алиса, 25, Москва # По парам (самый полезный) for key, value in user.items(): print(f"{key}: {value}") # name: Алиса # age: 25 # city: Москва ```

Практические примеры

Обработка данных

```python prices = {"apple": 100, "banana": 80, "orange": 120} # Найти самый дорогой товар max_item = max(prices, key=prices.get) print(f"Самый дорогой: {max_item}") # orange # Общая сумма total = sum(prices.values()) print(f"Всего: {total}") # 300 # Скидка 10% на все discounted = {k: v * 0.9 for k, v in prices.items()} print(discounted) # {'apple': 90.0, 'banana': 72.0, ...} ```

Что выводит цикл: for x in {"a": 1, "b": 2}?

Вложенные структуры

Словари могут содержать другие словари и списки - это основа для работы с JSON и сложными данными.

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

Как в JSON

```python user = { "name": "Алиса", "age": 25, "address": { "city": "Москва", "street": "Тверская", "building": 10 }, "hobbies": ["чтение", "программирование", "музыка"] } # Доступ к вложенным данным print(user["address"]["city"]) # Москва print(user["hobbies"][0]) # чтение # Изменение вложенных user["address"]["building"] = 15 user["hobbies"].append("спорт") ```

Список словарей

База данных в миниатюре

```python users = [ {"id": 1, "name": "Алиса", "active": True}, {"id": 2, "name": "Боб", "active": False}, {"id": 3, "name": "Вика", "active": True} ] # Найти активных пользователей active_users = [u["name"] for u in users if u["active"]] print(active_users) # ['Алиса', 'Вика'] # Найти пользователя по id def find_user(users, user_id): for user in users: if user["id"] == user_id: return user return None print(find_user(users, 2)) # {'id': 2, 'name': 'Боб', ...} ```

Как получить 'x' из: d = {"a": {"b": {"c": "x"}}}?

Dict Comprehensions

Как list comprehensions, но для словарей. Элегантный способ создания и трансформации.

Dict comprehension

Создание словаря в одну строку

```python # Квадраты чисел squares = {x: x**2 for x in range(5)} print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16} # С условием even_squares = {x: x**2 for x in range(10) if x % 2 == 0} print(even_squares) # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64} # Из списка names = ["Alice", "Bob", "Charlie"] name_lengths = {name: len(name) for name in names} print(name_lengths) # {'Alice': 5, 'Bob': 3, 'Charlie': 7} # Инверсия словаря (ключ ↔ значение) original = {"a": 1, "b": 2, "c": 3} inverted = {v: k for k, v in original.items()} print(inverted) # {1: 'a', 2: 'b', 3: 'c'} ```

Что создаст: {x: x*2 for x in [1, 2, 3]}?

Связь с другими темами

Словари и объекты опираются на списки и циклы, и ведут к JSON, паттернам и тестам:

  • Циклы — Итерация по .items(), .keys(), .values()
  • Функции — **kwargs распаковываются в dict, словари передают конфигурацию
  • Массивы и списки — list of dicts это типичная форма JSON, dict часто содержит list
  • Паттерны проектирования — Strategy и Registry часто строятся на словарях имя-объект
  • Тестирование — Fixtures и моки часто описываются словарями

Итог

  • Sketchpad Сазерленда (MIT, 1963) задал идею объекта, Simula 67 (Нюгор и Даль) дала классы, наследование и виртуальные методы
  • Smalltalk Алана Кэя в Xerox PARC (1972-1980) ввёл everything is an object и сообщения, MVC и рефакторинг родились там
  • C++ (Страуструп, 1983) и Java (Гослинг, 1995) принесли OOP в индустрию, JavaScript (Эйх, 1995) пошёл через прототипы, классы добавили только в ES6 (2015)
  • Python с 1991 года: всё объект, dict реализует namespace модуля и __dict__ экземпляра, type(x) работает для любого значения
  • С Python 3.7 (2017) dict сохраняет порядок вставки, dataclasses (PEP 557, Eric V. Smith, 2018) убирают бойлерплейт в __init__/__repr__/__eq__

Связанные уроки

  • prog-11-arrays — Массивы индексируют по позиции объекты по ключу
  • prog-07-loops — Перебор ключей объекта использует циклы
  • prog-13-patterns — Паттерны проектирования организуют объекты и их связи
  • ds-06-hash-intro — Объект это хеш-таблица ключей и значений
  • db-02-relational-model — Запись в таблице сопоставляет поля как объект
  • alg-01-big-o
Объекты и словари

0

1

Войти