※注意
本記事はstreamlit version 0.84.0以前の記事となります。
Streamlit version 0.84.0にてSession State機能がリリースされたため、本記事で行っているようなSession管理を自前実装する必要はなくなりました。
詳細は公式ドキュメントを参照ください: https://docs.streamlit.io/en/stable/add_state_app.html
こんにちは。
streamlitを使っているとき、その挙動の特殊さに苦労することが多いです。
今回は変数の状態維持についてどのように実現するかを書きます。
github: https://github.com/irisu-inwl/streamlit-state-test
現象
ボタンを押すとcountが増加、減退するアプリケーションを作ろうとします。
以下のようにコードを書くと、一回目の施行は成功しますが、以降ボタンを押しても変数は+1
, -1
から動きません。
import streamlit as st
def main():
st.title('Counter App')
count = 0
increment_count = st.button('count +')
decrement_count = st.button('count -')
if increment_count:
count += 1
if decrement_count:
count -= 1
st.write(f'count: {count}')
if __name__ == '__main__':
main()
run app
docker run -itd -v $(pwd)/src:/opt/streamlit/src -p 8080:8501 --name streamlit-state streamlit-state streamlit run src/wrong_app.py
結果:
streamlitの実行順序
streamlitの実行順序をおさらいします。
streamlitの公式ドキュメントでは、streamlitの挙動として、アプリのrendering後にユーザーによるイベント発火でスクリプトを再実行するとあります。
そのため、ボタンを押した際に毎回count = 0
が実行され変数が初期化されてしまい、+1
,-1
の状態となってしまいます。
対処
では、変数の状態を維持するためにはどうするか、というと、以下のstreamlit communityの記事にてセッションの状態を管理する方法が記されております。
引用されてるコードにある_SessionState
クラスを利用して、先ほどのアプリを以下のように改良します。
import streamlit as st
from src.session import _get_state
state = _get_state()
if state.count == None:
state.count = 0
def main():
st.title('Counter App')
increment_count = st.button('count +')
decrement_count = st.button('count -')
if increment_count:
state.count += 1
if decrement_count:
state.count -= 1
st.write(f'count: {state.count}')
if __name__ == '__main__':
main()
まず、_SessionState
クラス, _get_state()
, _get_session()
をsession.py
に書きます。(こんな感じ)
そして、アプリケーション実行時にcount
を初期化します。その際に_SessionState
にcount変数の状態を保存するためstate.count
を参照することで、アプリケーションのイベントが発生で初期化されないようにします。
以降アプリケーションコードで状態を管理したい変数はstate
から参照すれば以下のように状態が保持されます。
おわり
streamlitで変数の状態を管理する方法を紹介しました。
streamlitはめちゃめちゃ便利ですが、使う上で、その挙動について知ることと、何が出来て何ができないかを知ることが大切だと思います。(任意のことにも言えますが)
では。