Avancé

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 :

python
# É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 :

python
# 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.

python
# 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

python
# 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
python
# 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 :

python
# 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

python
# 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éeMé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_resourceRessources partagées, ne doivent pas être dupliquées par session
Données persistantes (survie au redémarrage)Base de données ou fichiersession_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_state est 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 key sur un widget le lie automatiquement à session_state
  • Utiliser session_state pour 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)

Ressources