Производительность и архитектура: кэш, Session State, компоненты
Streamlit удобен тем, что вы пишете обычный Python-код, а интерфейс строится автоматически. Но у этого есть цена: при любом действии пользователя скрипт часто перезапускается целиком. Если приложение читает файлы, ходит в API или считает тяжёлые агрегации, без правильной архитектуры оно быстро станет медленным и неудобным.
Эта тема связывает всё, что вы уже изучили:
вы умеете строить интерфейс на виджетах и формах
вы умеете отображать таблицы, графики и метрики
вы умеете загружать данные и обрабатывать их в pandasТеперь вы научитесь делать это быстро и поддерживаемо:
правильно использовать st.session_state для пользовательского состояния
кэшировать загрузку/обработку через st.cache_data
кэшировать «ресурсы» (подключения, модели) через st.cache_resource
собирать приложение из компонентов (модулей и функций), чтобы было проще расширять проект!Диаграмма показывает, как перезапуски Streamlit сочетаются с состоянием и кэшем
Почему Streamlit “тормозит” и что с этим делать
Причины медленной работы обычно типовые:
на каждом перезапуске вы заново читаете большой CSV или Excel
на каждом перезапуске вы снова делаете запрос к API
на каждом перезапуске вы повторяете одинаковую обработку pandas
вы создаёте тяжёлые объекты заново (подключение к БД, модель ML)Правильная стратегия:
пользовательское состояние хранить в st.session_state
данные и вычисления кэшировать через st.cache_data
долгоживущие ресурсы кэшировать через st.cache_resourceОфициальные ссылки:
st.session_state
st.cache_data
st.cache_resourceSession State как слой состояния приложения
Что хранить в st.session_state
st.session_state нужен для всего, что пользователь ожидает сохранить между перезапусками:
текущие значения виджетов по ключам key
результаты действий пользователя (добавленные записи, выбранные элементы)
“режимы” интерфейса (например, показывать ли расширенный отчёт)Неудачная идея: складывать в st.session_state всё подряд, включая большие датафреймы, если их можно получить из кэша. На практике лучше держать в состоянии то, что не является чисто вычисляемым и зависит от действий пользователя.
Паттерн: инициализация значений по умолчанию
Дальше вы можете обновлять этот словарь из обработчиков или из формы.
Паттерн: “событие” отдельно от “данных”
Кнопка st.button даёт True только на перезапуске клика. Поэтому удобно делать так:
кнопка меняет состояние
интерфейс рисуется из состоянияКэш данных: st.cache_data
Что делает st.cache_data
st.cache_data кэширует результат функции по её входным параметрам. Если функция вызывается снова с теми же аргументами, Streamlit возвращает сохранённый результат, а не пересчитывает.
Используйте st.cache_data для:
чтения CSV/Excel
запросов к API
очистки/преобразования данных в pandas
вычисления агрегатов и сводокПример: кэшируем загрузку и очистку.
TTL: кэш с “временем жизни”
Если источник данных меняется (например, API), задайте ttl в секундах.
Идея простая: в течение 60 секунд повторные вызовы будут быстрыми, а потом данные обновятся.
Важное правило: кэшируйте чистые функции
Функция хорошо кэшируется, если:
результат зависит только от входных аргументов
внутри нет скрытых зависимостей от глобального состояния и случайностиПлохие кандидаты на кэш:
функция читает st.session_state внутри себя
функция зависит от текущего времени без ttl
функция меняет внешний мир (пишет в файл, отправляет данные)Кэш ресурсов: st.cache_resource
st.cache_resource предназначен не для данных, а для тяжёлых объектов, которые создаются долго, но потом многократно используются:
подключение к базе данных
клиент внешнего сервиса
загруженная ML-модель
объект, который держит пул соединенийПример: кэшируем создание “клиента” (упрощённо).
Таблица выбора:
| Задача | Что использовать | Что кэшируется |
|---|---|---|
| Прочитать CSV и вернуть DataFrame | st.cache_data | результат (данные) |
| Сходить в API и вернуть JSON | st.cache_data | результат (данные) |
| Держать подключение/клиент/модель | st.cache_resource | объект-ресурс |
| Хранить действия пользователя | st.session_state | состояние сессии |
Архитектура приложения: разделяем UI, данные и логику
Когда приложение растёт, главный источник проблем не графики и не виджеты, а “всё в одном файле”. Хорошая учебная архитектура для проекта строится вокруг трёх слоёв:
UI слой: виджеты, табы, сайдбар, вывод
слой данных: загрузка, API, чтение файлов
бизнес-логика: очистка, фильтры, агрегатыМинимальная структура проекта
Роли модулей:
data.py содержит функции загрузки и получения данных (обычно с st.cache_data)
logic.py содержит чистые функции обработки pandas
ui.py содержит функции, которые рисуют части интерфейсаТак вы получаете “компоненты” на уровне Python: повторно используемые функции, которые легко тестировать и переносить.
Компоненты интерфейса: собираем страницу из блоков
В контексте учебного проекта под компонентами удобно понимать два уровня:
функциональные компоненты как Python-функции, рисующие часть страницы
переиспользуемые блоки интерфейса через контейнеры Streamlit (st.container, st.expander, st.tabs, st.sidebar)Если вы хотите настоящие кастомные компоненты (React/JS), у Streamlit есть отдельный механизм.
Custom ComponentsПаттерн: UI-функции, которые принимают данные и возвращают выбор
Идея: render_filters отвечает только за UI и возвращает параметры, а render_report отвечает только за отображение.
Паттерн: контейнеры для управляемой перерисовки
Streamlit часто удобно “собирать” в логические блоки:
st.sidebar для настроек
st.tabs для больших разделов
st.expander для второстепенных деталейСквозной пример: быстрое приложение “Импорт → фильтры → отчёт”
Ниже каркас, который повторяет будущий проектный сценарий, но с акцентом на архитектуру и производительность.
Что здесь сделано правильно:
чтение и базовая очистка кэшируются (st.cache_data), поэтому не повторяются на каждый клик
фильтры и отображение разделены по функциям
если файла нет, выполнение останавливается через st.stop(), и лишний код не выполняетсяЧастые ошибки производительности и как их исправить
Загрузка файла или запрос к API происходит на каждый перезапуск
- исправление: вынесите в функцию с
st.cache_data
Тяжёлый объект создаётся каждый раз заново
- исправление: создавайте его в
st.cache_resource
Смешаны UI и обработка данных в одном месте
- исправление: вынесите обработку в чистые функции (их легче кэшировать и тестировать)
Состояние пользователя хранится в обычных переменных
- исправление: переносите долгоживущие значения в
st.session_stateМини-чеклист перед проектом
Вы понимаете, что st.session_state хранит пользовательское состояние между перезапусками.
Вы используете st.cache_data для данных и вычислений, которые повторяются.
Вы используете st.cache_resource для ресурсов, которые долго создаются и долго живут.
Вы разделяете код на компоненты: функции/модули для UI, загрузки данных и логики.