Working with fragments

重新运行是每个Streamlit应用程序的核心部分。当用户与小部件交互时,您的脚本会从头到尾重新运行,并且应用程序的前端会更新。Streamlit提供了多种功能,帮助您在此执行模型内开发应用程序。Streamlit版本1.37.0引入了片段功能,允许重新运行代码的一部分,而不是整个脚本。随着应用程序变得更大更复杂,这些片段重新运行有助于提高应用程序的效率和性能。片段为您提供了更精细、易于理解的应用程序执行流程控制。

在您阅读关于片段的内容之前,我们建议您对缓存会话状态表单有基本的了解。

片段非常多功能,适用于各种情况。以下是一些常见的场景,片段在这些场景中非常有用:

  • 您的应用程序有多个可视化图表,每个图表都需要时间加载,但您有一个过滤器输入,它只更新其中一个图表。
  • 你有一个动态表单,不需要更新应用程序的其余部分(直到表单完成)。
  • 您希望自动更新单个组件或组件组以流式传输数据。

Streamlit 提供了一个装饰器 (st.fragment) 来将任何函数转换为片段函数。当你调用包含小部件函数的片段函数时,用户与该片段的小部件交互时会触发片段重新运行,而不是完全重新运行。在片段重新运行期间,只有你的片段函数会被重新执行。片段主体内的任何内容都会在前端更新,而应用程序的其余部分保持不变。我们稍后会描述跨多个容器编写的片段。

这里是一个定义和调用片段函数的基本示例。就像缓存一样,记得在定义后调用你的函数。

import streamlit as st @st.fragment def fragment_function(): if st.button("Hi!"): st.write("Hi back!") fragment_function()

如果你希望片段的主体出现在侧边栏或其他容器中,请在上下文管理器中调用你的片段函数。

with st.sidebar: fragment_function()

考虑以下代码及其解释和图表。

import streamlit as st st.title("My Awesome App") @st.fragment() def toggle_and_text(): cols = st.columns(2) cols[0].toggle("Toggle") cols[1].text_area("Enter text") @st.fragment() def filter_and_file(): cols = st.columns(2) cols[0].checkbox("Filter") cols[1].file_uploader("Upload image") toggle_and_text() cols = st.columns(2) cols[0].selectbox("Select", [1,2,3], None) cols[1].button("Update") filter_and_file()

当用户与片段内的输入小部件交互时,只有片段会重新运行,而不是整个脚本。当用户与片段外的输入小部件交互时,整个脚本会像往常一样重新运行。

如果你运行上面的代码,整个脚本将在你的应用程序初始加载时从上到下运行。如果你在运行的应用程序中切换切换按钮,第一个片段(toggle_and_text())将重新运行,重新绘制切换按钮和文本区域,而其他所有内容保持不变。如果你点击复选框,第二个片段(filter_and_file())将重新运行,从而重新绘制复选框和文件上传器。其他所有内容保持不变。最后,如果你点击更新按钮,整个脚本将重新运行,Streamlit 将重新绘制所有内容。

Diagram of fragment execution flow

Streamlit 在片段重新运行时忽略片段的返回值,因此不建议为您的片段函数定义返回值。相反,如果您的片段需要与应用程序的其余部分共享数据,请使用会话状态。片段只是脚本中的函数,因此它们可以访问会话状态、导入的模块以及其他 Streamlit 元素(如容器)。如果您的片段写入到自身之外创建的任何容器,请注意以下行为差异:

  • 在片段重新运行时,片段主体中绘制的元素会被清除并在原地重新绘制。重复的片段重新运行不会导致出现额外的元素。
  • 绘制到片段主体外部的容器的元素不会在每次片段重新运行时被清除。相反,Streamlit 会以累加的方式绘制它们,这些元素将累积直到下一次完整脚本重新运行。
  • 片段无法在片段主体之外的容器中绘制小部件。小部件只能放置在片段的主体中。

为了防止元素在外部容器中积累,请使用st.empty容器。有关相关教程,请参阅跨多个容器创建片段

如果你需要从片段内部触发整个脚本的重新运行,请调用st.rerun。有关相关教程,请参阅从片段内部触发整个脚本的重新运行

st.fragment 包含一个方便的 run_every 参数,该参数使片段在指定的时间间隔内自动重新运行。这些重新运行是在用户触发的任何重新运行(片段或完整脚本)之外的。即使您的用户没有与您的应用程序交互,自动片段重新运行也会继续。这是在运行的后台作业上显示实时数据流或状态的好方法,有效地更新您的渲染数据,并且更新您的渲染数据。

@st.fragment(run_every="10s") def auto_function(): # This will update every 10 seconds! df = get_latest_updates() st.line_chart(df) auto_function()

有关教程,请参阅启动和停止流片段

以下是片段和表单之间的比较:

  • 表单允许用户与小部件交互而无需重新运行您的应用程序。在表单提交之前,Streamlit不会将表单内的用户操作发送到您的应用程序的Python后端。表单内的小部件无法实时动态更新其他小部件(无论是在表单内还是表单外)。
  • 片段独立于代码的其余部分运行。当用户与片段小部件交互时,他们的操作会立即由应用程序的Python后端处理,并且片段代码会重新运行。片段内的小部件可以实时动态更新同一片段内的其他小部件。

表单批量处理用户输入,而无需任何小部件之间的交互。片段立即处理用户输入,但限制了重新运行的范围。

以下是片段和回调之间的比较:

  • 回调函数 允许你在脚本重新运行时执行一个函数。回调函数是脚本重新运行的 单一前缀
  • 片段 允许你重新运行脚本的一部分。片段是脚本的 可重复后缀,每次用户与片段小部件交互时运行,或者在 run_every 设置时自动按顺序运行。

当回调将元素渲染到您的页面时,它们会在页面其他元素之前渲染。当片段将元素渲染到您的页面时,它们会在每次片段重新运行时更新(除非它们被写入片段之外的容器中,在这种情况下它们会在那里累积)。

以下是片段和自定义组件之间的比较:

  • 组件是可以与Python代码、原生元素和Streamlit应用中的小部件交互的自定义前端代码。自定义组件扩展了Streamlit的可能性。它们遵循正常的Streamlit执行流程。
  • Fragments 是应用程序中可以独立于整个应用程序重新运行的部分。Fragments 可以由多个 Streamlit 元素、小部件或任何 Python 代码组成。

一个片段可以包含一个或多个自定义组件。一个自定义组件不容易包含一个片段!

以下是片段和缓存之间的比较:

  • 缓存: 允许你跳过函数并返回之前计算的值。当你使用缓存时,除了缓存的函数(如果你之前已经运行过它),其他所有内容都会执行。
  • 片段: 允许你冻结应用的大部分内容,只执行片段。当你使用片段时,你只执行片段(当触发片段重新运行时)。

缓存可以避免在应用程序的其余部分运行时不必要地运行应用程序的一部分。片段可以避免在您只想运行应用程序的一部分时运行整个应用程序。

  • 片段无法检测输入值的变化。对于片段函数的动态输入和输出,最好使用会话状态。
  • 在同一函数上使用缓存和片段是不支持的。
  • 片段无法在外部创建的容器中渲染小部件;小部件只能位于片段的主体部分。
forum

还有问题吗?

我们的 论坛 充满了有用的信息和Streamlit专家。