Тема 11: Структура данных: Почему dict — это зло, и зачем обязательно использовать Pydantic для парсинга?
Новички любят словари (dict).
"О, сервер вернул JSON, я просто сделаю data = response.json() и буду обращаться по ключам!"
Это мина замедленного действия. Пока скрипт маленький — это работает. Когда скрипт разрастается, словари превращают код в помойку, которую невозможно поддерживать.
1. Почему dict — это боль? (Синдром "Угадай Ключ")
Представь код:
def process_item(item):
# Что такое item? Словарь. Какие там ключи? ХЗ.
# Надо лезть в функцию парсинга или принтовать item, чтобы узнать.
# Ошибка 1: Опечатка в ключе. Узнаешь только в рантайме.
name = item['title'] # А на сайте сменили ключ на 'name' -> KeyError
# Ошибка 2: Типы данных.
price = item['price'] # Пришло "1000 руб" (str), а ты ждал 1000 (int)
total = price * 2 # "1000 руб1000 руб" -> Логическая ошибка, скрипт не упал, но данные мусор.
Проблемы:
- Нет автодополнения (Autocomplete): IDE не знает, что лежит в словаре. Ты пишешь код вслепую.
- Нет валидации: Ты тащишь грязные данные (пробелы,
null, строки вместо чисел) глубоко в бизнес-логику и базу данных. - Хрупкость: Сменился API — всё упало в случайных местах.
2. Pydantic — "Железобетонный Контракт"
Pydantic — это библиотека, которая заставляет данные отвечать за базар. Ты описываешь Схему (класс), и библиотека сама проверяет, чистит и типизирует входящий мусор.
Как это выглядит:
from pydantic import BaseModel, HttpUrl, field_validator
from typing import Optional
# Мы описываем КОНТРАКТ.
# Если данные не подходят — они не пройдут дальше порога.
class Product(BaseModel):
id: int # Сам сконвертирует "123" в 123
title: str
price: float # Сам сделает float
url: HttpUrl # Проверит, что это валидная ссылка
is_available: bool = True # Дефолтное значение
# Можно писать свои чистильщики (Validators)
@field_validator('title')
def clean_title(cls, v):
return v.strip().upper() # Убрали пробелы, капсом
# --- В БОЮ ---
raw_data = {
"id": "555", # Строка!
"title": " iphone ", # Пробелы!
"price": 100.50,
"url": "<https://apple.com>"
}
# Магия:
item = Product(**raw_data)
print(item.id) # 555 (int) - Тип исправлен!
print(item.title) # "IPHONE" - Очищено!
print(item.price) # 100.5 (float)
# IDE подсказывает: item. (и выпадают поля id, title, price)
3. Почему Архитектор требует Pydantic?
- Fail Fast (Падай быстро):
Если сайт изменил API и начал слать цену как
null, Pydantic выкинет ошибку сразу при создании объекта. Ты узнаешь о проблеме на этапе парсинга, а не когда будешь записывать данные в БД и получишь краш. - IDE — твой друг:
Когда ты пишешь
item.price, IDE знает, что этоfloat. Она подсветит ошибку, если ты попробуешь сделатьitem.price.upper(). В словарях IDE молчит как партизан. -
Вложенность (Nesting): JSON часто бывает сложным (список вариантов, характеристики). На Pydantic это описывается красиво:
class Variant(BaseModel): color: str size: str class Product(BaseModel): name: str variants: list[Variant] # Список объектов, а не список словарей!Теперь ты обращаешься
product.variants[0].color. Чистота. -
Экспорт: Нужно сохранить в файл?
item.model_dump_json()— и у тебя готовая JSON-строка.item.model_dump()— готовый словарь для вставки в MongoDB.
🧠 Резюме:
- Использовать
dictдля передачи данных между функциями — это говнокод. - Любой парсер должен начинаться с файла
models.py, где описаны классы данных. - Парсинг выглядит так:
- Получил грязный HTML/JSON.
- Вытащил сырые данные.
- Тут же запихнул их в Pydantic-модель.
- Дальше работаешь ТОЛЬКО с объектом модели.