Streamlit 多页应用程序中的会话状态已重置

Mvd*_*vdD 9 python session-state streamlit

我正在构建 Streamlit 多页面应用程序,但在页面之间切换时保持会话状态时遇到问题。我的主页称为 mainpage.py ,内容如下:

import streamlit as st

if "multi_select" not in st.session_state:
    st.session_state["multi_select"] = ["abc", "xyz"]
if "select_slider" not in st.session_state:
    st.session_state["select_slider"] = ("1", "10")
if "text_inp" not in st.session_state:
    st.session_state["text_inp"] = ""

st.sidebar.multiselect(
    "multiselect",
    ["abc", "xyz"],
    key="multi_select",
    default=st.session_state["multi_select"],
)

st.sidebar.select_slider(
    "number range",
    options=[str(n) for n in range(1, 11)],
    key="select_slider",
    value=st.session_state["select_slider"],
)
st.sidebar.text_input("Text:", key="text_inp")

for v in st.session_state:
    st.write(v, st.session_state[v])
Run Code Online (Sandbox Code Playgroud)

接下来,我在名为“pages”的子目录中有另一个名为“anotherpage.py”的页面,其内容如下:

import streamlit as st

for v in st.session_state:
    st.write(v, st.session_state[v])
Run Code Online (Sandbox Code Playgroud)

如果我运行此应用程序,更改控件的值并切换到其他页面,我会看到控件的值被保留并打印。但是,如果我切换回主页,所有内容都会重置为原始值。由于某种原因st.session_state被清除。

有人知道如何将值保持在会话状态吗?我正在使用 Python3.11.1和 Streamlit1.16.0

小智 5

首先,了解小部件的生命周期很重要。当您为小部件分配一个键时,只要该小部件未呈现,该键就会从会话状态中删除。如果小部件有条件地不在同一页面上或通过切换页面呈现,则可能会发生这种情况。

您在第二页上看到的是小部件清理过程完成之前上一页剩余的值。在加载“另一页”结束时,Streamlit 意识到它在会话状态中的键分配给了已经消失的小部件,因此将其删除。

有两种方法可以解决这个问题。

  1. 一个 hacky 解决方案(不是我的偏好)是将值重新提交到每个页面顶部的会话状态。
st.session_state.my_widget_key = st.session_state.my_widget_key
Run Code Online (Sandbox Code Playgroud)

这将中断小部件清理过程并防止键被删除。但是,它需要位于您离开小部件时访问的页面上。因此,它需要出现在所有页面上。

  1. 我首选的解决方案是将小部件键与我想要保留的值分开。我通常采用在小部件键前添加下划线的约定。
import streamlit as st

if "multi_select" not in st.session_state:
    st.session_state["multi_select"] = ["abc", "xyz"]
if "select_slider" not in st.session_state:
    st.session_state["select_slider"] = ("1","10")
if "text_inp" not in st.session_state:
    st.session_state["text_inp"] = ""

def keep(key):
    # Copy from temporary widget key to permanent key
    st.session_state[key] = st.session_state['_'+key]

def unkeep(key):
    # Copy from permanent key to temporary widget key
    st.session_state['_'+key] = st.session_state[key]

unkeep("multi_select")
st.sidebar.multiselect(
    "multiselect",
    ["abc", "xyz"],
    key="_multi_select",
    on_change=keep,
    args=['multi_select']
)

# This is a edge case and possibly a bug. See explanation.
st.sidebar.select_slider(
    "number range",
    options=[str(n) for n in range(1, 11)],
    value = st.session_state.select_slider,
    key="_select_slider",
    on_change=keep,
    args=["select_slider"]
)

unkeep("text_inp")
st.sidebar.text_input("Text:", key="_text_inp", on_change=keep, args=["text_inp"])

for v in st.session_state:
    st.write(v, st.session_state[v])
Run Code Online (Sandbox Code Playgroud)

您会发现我对选择滑块做了不同的操作。看来需要value专门将一个元组传递给 kwarg,以确保它初始化为范围滑块。如果使用单个值而不是范围值初始化它,我就不需要更改逻辑。对于其他小部件,您可以看到默认值已被删除,有利于通过会话状态中的键直接控制它们的值。

当您执行更改小部件默认值的操作时需要小心。对默认值的更改会创建一个“新小部件”。如果您通过其键同时更改默认值和实际值,则可能会出现一些细微的行为,例如在发生冲突时出现初始化警告。