サイトアイコン アマチュア無線局JS2IIU

【Streamlit】st.session_state に辞書・リストなどの複雑なオブジェクトを持たせる方法と実践例

こんにちは、JS2IIUです。
今回は、Streamlitの便利な機能 st.session_state に、単純な値だけでなく、辞書やリストなどの複雑なデータを持たせる方法を丁寧に解説します。

1. そもそも st.session_state って何?

StreamlitはWebアプリを簡単に作れるPythonのライブラリですが、ページが更新されるたびにプログラムは最初から実行されます。
そのため、ユーザーの操作で入力した値やデータは普通に書くだけでは保持されません。

ここで役立つのが st.session_state です。
これは「セッション(接続)ごとの状態を保存しておく辞書のようなもの」と考えてください。

これにより、ユーザー体験を途切れさせずにアプリを作れます。

2. st.session_state に単純な値(スカラー値)を保存する基本例

まずは、簡単な文字列や数値の保存例を見てみましょう。

Python
import streamlit as st

# 初期値を設定(キーがなければ)
if 'counter' not in st.session_state:
    st.session_state.counter = 0

# ボタンを押したらカウンターを増やす
if st.button('カウントアップ'):
    st.session_state.counter += 1

st.write(f"カウンターの値:{st.session_state.counter}")

このコードでは、counter という単純な数値を st.session_state に保存しています。
ボタンを押すたびに増えていくのが分かりますね。

3. 辞書やリストなど複雑なオブジェクトを持たせたい理由

しかし、実際のアプリでは、もっと複雑な情報を一時的に保存したいことが多いです。

こうした時には、辞書やリストを使ってデータ構造を作り、st.session_state に保存します。

4. 辞書やリストを st.session_state に持たせる方法【基本編】

4-1. 初期化は必ずやろう

st.session_state はPythonの辞書に似ていますが、キーが存在しないとエラーになるので、まずは初期化が大事です。

Python
if 'form_data' not in st.session_state:
    st.session_state.form_data = {}

このように、キーがなければ空の辞書をセットします。

4-2. 辞書やリストの更新は「直接変更」より「代入」が安全

Pythonの辞書やリストは「ミュータブル(変更可能)」ですが、Streamlitは状態の変化を検知するために「新しいオブジェクトを代入する」ことを推奨しています。

Python
# NG例(動く場合もありますが推奨されません)
st.session_state.form_data['name'] = '太郎'

# OK例(辞書をコピーしてから変更)
data = st.session_state.form_data.copy()
data['name'] = '太郎'
st.session_state.form_data = data

5. 実践例1:複数ステップの入力フォームを作る

5-1. 何を作る?

ユーザーが「名前」「年齢」「趣味」を別々のステップで入力し、全体を st.session_state の辞書にまとめて保存する例です。

5-2. コード全体

Python
import streamlit as st

# 初期化
if 'form_data' not in st.session_state:
    st.session_state.form_data = {}

if 'step' not in st.session_state:
    st.session_state.step = 1

def next_step():
    st.session_state.step += 1

def prev_step():
    st.session_state.step -= 1

st.title("複数ステップ入力フォーム")

# ステップごとに表示するフォーム
if st.session_state.step == 1:
    name = st.text_input("名前を入力してください", st.session_state.form_data.get('name', ''))
    if st.button("次へ"):
        data = st.session_state.form_data.copy()
        data['name'] = name
        st.session_state.form_data = data
        next_step()

elif st.session_state.step == 2:
    age = st.number_input("年齢を入力してください", min_value=0, max_value=120, value=st.session_state.form_data.get('age', 0))
    col1, col2 = st.columns(2)
    with col1:
        if st.button("戻る"):
            prev_step()
    with col2:
        if st.button("次へ"):
            data = st.session_state.form_data.copy()
            data['age'] = age
            st.session_state.form_data = data
            next_step()

elif st.session_state.step == 3:
    hobby = st.text_input("趣味を入力してください", st.session_state.form_data.get('hobby', ''))
    col1, col2 = st.columns(2)
    with col1:
        if st.button("戻る"):
            prev_step()
    with col2:
        if st.button("送信"):
            data = st.session_state.form_data.copy()
            data['hobby'] = hobby
            st.session_state.form_data = data
            st.success("送信完了!")
            st.write("入力内容の確認")
            st.json(st.session_state.form_data)
https://js2iiu.com/wp-content/uploads/2025/05/04_02.mov

5-3. 解説

6. 実践例2:表形式データの仮保存(リストのリストを管理)

6-1. 何を作る?

複数の商品の名前と価格を入力し、一時的にリストとして保存し画面で表示・編集できるシンプルなアプリです。

6-2. コード全体

Python
import streamlit as st

# 初期化(商品リストは空リスト)
if 'items' not in st.session_state:
    st.session_state.items = []

st.title("商品リストの編集")

with st.form("add_item_form"):
    name = st.text_input("商品名")
    price = st.number_input("価格", min_value=0, step=1)
    submitted = st.form_submit_button("追加")

if submitted:
    items = st.session_state.items.copy()
    items.append({"name": name, "price": price})
    st.session_state.items = items
    st.success(f"商品「{name}」を追加しました!")

# 商品一覧表示
if st.session_state.items:
    st.write("現在の登録商品:")
    for i, item in enumerate(st.session_state.items):
        col1, col2, col3 = st.columns([4, 2, 1])
        col1.write(item['name'])
        col2.write(f"{item['price']} 円")
        if col3.button("削除", key=f"del_{i}"):
            items = st.session_state.items.copy()
            items.pop(i)
            st.session_state.items = items
            st.rerun()  # 再実行して表示更新
else:
    st.write("商品がまだ登録されていません。")

6-3. 解説

7. まとめと注意点

参考

最後まで読んでいただきありがとうございます。

モバイルバージョンを終了