Persistance de l'état
Le problème de la mémoire courte
On l'a vu en séance 2 : à chaque interaction, Streamlit réexécute le script Python entièrement. Toutes les variables locales sont réinitialisées. En général, c'est le bon comportement — les widgets retrouvent leur valeur grâce au mécanisme interne de Streamlit.
Mais certains cas nécessitent de conserver une information d'un re-run à l'autre sans qu'elle soit directement attachée à un widget :
- L'utilisateur a cliqué sur une barre d'un graphique — on veut se souvenir de quelle région a été sélectionnée pour filtrer d'autres composants
- L'utilisateur navigue entre pages — on veut que les filtres choisis sur la page 1 soient encore actifs sur la page 2
- On veut compter le nombre de fois qu'un bouton a été cliqué
- On accumule des sélections dans une liste (un "panier" de métriques à comparer)
Définition
st.session_state est un dictionnaire persistant entre les re-runs, propre à chaque session utilisateur. Il survit aux re-runs mais pas aux redémarrages de l'application.
Syntaxe de base
Deux notations interchangeables :
# Écrire dans session_state
st.session_state["region_selectionnee"] = "IDF"
# Lire depuis session_state
region = st.session_state.get("region_selectionnee", "Toutes")
# Vérifier si une clé existe
if "region_selectionnee" in st.session_state:
...
# Notation attribut (équivalente à la notation dictionnaire)
st.session_state.region_selectionnee = "IDF"
region = st.session_state.region_selectionnee
Quelle notation choisir ?
La notation dictionnaire est préférable pour les clés dynamiques ou les noms avec des caractères spéciaux. La notation attribut est plus concise pour les clés simples.
Initialisation — éviter les KeyError
Initialisez les clés de session_state en début de script pour éviter les erreurs au premier chargement :
# Pattern d'initialisation recommandé
if "region_selectionnee" not in st.session_state:
st.session_state["region_selectionnee"] = "Toutes"
if "historique_recherches" not in st.session_state:
st.session_state["historique_recherches"] = []
Ce bloc s'exécute à chaque re-run mais n'écrase la valeur que si la clé n'existe pas encore. C'est le pattern standard à utiliser systématiquement.
Erreur fréquente
Sans ce pattern d'initialisation, le premier chargement de la page provoque un KeyError car la clé n'existe pas encore dans session_state.
Lier un widget à session_state avec key
Les widgets Streamlit acceptent un paramètre key qui les lie à session_state. La valeur du widget est alors accessible via st.session_state[key] et persiste entre les re-runs.
# Sans key — valeur accessible uniquement via la variable locale
region = st.selectbox("Région", options=["Toutes", "IDF", "PACA"])
# Avec key — valeur accessible via session_state ET via la variable locale
region = st.selectbox("Région", options=["Toutes", "IDF", "PACA"], key="filtre_region")
# st.session_state["filtre_region"] contient la même valeur que region
L'avantage du key : la valeur est accessible depuis n'importe quelle page de l'application, pas seulement là où le widget est défini. C'est ce qui permet de partager des filtres entre pages.
Quel est l'avantage principal du paramètre key sur un widget Streamlit ?
Cas d'usage 1 — filtres persistants entre pages
# pages/1_Ventes.py
import streamlit as st
# Initialisation
if "annee" not in st.session_state:
st.session_state["annee"] = 2024
# Widget lié à session_state
annee = st.sidebar.slider("Année", 2020, 2024, key="annee")
# L'utilisateur navigue vers la page 2
# st.session_state["annee"] conserve la valeur choisie
# pages/2_Statistiques.py
import streamlit as st
# Lire la valeur choisie sur la page 1
annee = st.session_state.get("annee", 2024)
st.write(f"Statistiques pour l'année : {annee}")
Grâce au key="annee" sur le slider de la page 1, la valeur choisie est automatiquement disponible sur la page 2 via st.session_state["annee"].
Cas d'usage 2 — sélection croisée entre graphiques
On a vu en séance 3 que st.plotly_chart() peut capturer les clics utilisateur via on_select="rerun". session_state sert à stocker la sélection et l'utiliser dans d'autres composants :
# Initialisation
if "region_cliquee" not in st.session_state:
st.session_state["region_cliquee"] = None
# Graphique avec capture de sélection
fig = px.bar(df_regions, x="region", y="chiffre_affaires")
event = st.plotly_chart(fig, use_container_width=True, on_select="rerun")
# Mettre à jour session_state si l'utilisateur a cliqué
if event and event.selection.points:
st.session_state["region_cliquee"] = event.selection.points[0]["x"]
# Utiliser la sélection pour filtrer un autre composant
region_active = st.session_state["region_cliquee"]
if region_active:
st.subheader(f"Détail — {region_active}")
df_detail = client.get_ventes(region=region_active)
st.dataframe(df_detail, use_container_width=True)
else:
st.info("Cliquez sur une région dans le graphique pour voir le détail.")
Cas d'usage 3 — compteur et accumulation
# Compter les appels API (utile pour le debug ou les métriques)
if "nb_appels_api" not in st.session_state:
st.session_state["nb_appels_api"] = 0
# Incrémenter à chaque appel
st.session_state["nb_appels_api"] += 1
st.caption(f"Appels API cette session : {st.session_state['nb_appels_api']}")
Utile pour le débogage ou pour afficher des métriques de session.
Ce qu'il ne faut pas stocker dans session_state
session_state est en mémoire, propre à une session navigateur, et perdu au redémarrage de l'application. Certaines données méritent un autre mécanisme :
| Donnée | Mécanisme adapté | Pourquoi |
|---|---|---|
| Grands DataFrames | @st.cache_data | Évite de recharger les données à chaque re-run, partagé entre sessions |
| Connexions DB, clients HTTP | @st.cache_resource | Ressources partagées, ne doivent pas être dupliquées par session |
| Données persistantes (survie au redémarrage) | Base de données ou fichier | session_state est perdu au redémarrage de l'app |
Ne pas confondre
session_state persiste entre les re-runs (interactions utilisateur), pas entre les redémarrages de l'application. Pour des données durables, utilisez une base de données.
À retenir
Points clés
st.session_stateest un dictionnaire persistant entre les re-runs, propre à chaque session utilisateur- Toujours initialiser les clés avec
if "clé" not in st.session_state - Le paramètre
keysur un widget le lie automatiquement àsession_state - Utiliser
session_statepour les filtres entre pages, les sélections croisées et les compteurs - Ne pas y stocker de grands DataFrames (utiliser
@st.cache_data) ni de ressources partagées (utiliser@st.cache_resource)