文章目录
- [一、Streamlit 架构与运行机制](#一、Streamlit 架构与运行机制)
-
- [1. 运行应用](#1. 运行应用)
-
- [1.1 基础运行命令](#1.1 基础运行命令)
- [1.2 向脚本传递自定义参数](#1.2 向脚本传递自定义参数)
- [2. 核心架构](#2. 核心架构)
-
- [2.1 架构概览](#2.1 架构概览)
- [2.2 服务端(Python Backend)](#2.2 服务端(Python Backend))
- [2.3 客户端(Browser Frontend)](#2.3 客户端(Browser Frontend))
- [2.4 架构对应用设计的影响](#2.4 架构对应用设计的影响)
- [2.5 WebSockets 与会话管理](#2.5 WebSockets 与会话管理)
- [2.6 会话亲和性](#2.6 会话亲和性)
- [3. 应用界面](#3. 应用界面)
-
- [3.1 应用菜单概览](#3.1 应用菜单概览)
- [3.2 通用功能详解](#3.2 通用功能详解)
- [3.3 开发者选项(Developer options)](#3.3 开发者选项(Developer options))
- [3.4 菜单自定义(Customize the menu)](#3.4 菜单自定义(Customize the menu))
- [4. 缓存机制](#4. 缓存机制)
-
- [4.1 缓存核心装饰器](#4.1 缓存核心装饰器)
- [4.2 数据结果缓存](#4.2 数据结果缓存)
- [4.3 全局资源缓存](#4.3 全局资源缓存)
- [4.4 缓存机制的工作原理](#4.4 缓存机制的工作原理)
- [4.5 关键使用注意事项](#4.5 关键使用注意事项)
- [4.6 自定义缓存行为](#4.6 自定义缓存行为)
- [5. 会话状态](#5. 会话状态)
-
- [5.1 什么是会话状态](#5.1 什么是会话状态)
- [5.2 为什么需要会话状态](#5.2 为什么需要会话状态)
- [5.3 会话状态的基本用法](#5.3 会话状态的基本用法)
- [5.4 回调函数进阶用法](#5.4 回调函数进阶用法)
- [5.5 会话状态与组件状态绑定](#5.5 会话状态与组件状态绑定)
- [5.6 关键注意事项与限制](#5.6 关键注意事项与限制)
- [6. 表单组件](#6. 表单组件)
-
- [6.1 表单的核心作用](#6.1 表单的核心作用)
- [6.2 表单的基础用法](#6.2 表单的基础用法)
- [6.3 表单的交互行为](#6.3 表单的交互行为)
- [6.4 表单容器的使用方式](#6.4 表单容器的使用方式)
- [6.5 表单提交的处理方式](#6.5 表单提交的处理方式)
-
- [6.5.1 提交后直接处理(推荐)](#6.5.1 提交后直接处理(推荐))
- [6.5.2 配合回调函数处理](#6.5.2 配合回调函数处理)
- [6.6 表单的关键限制与注意事项](#6.6 表单的关键限制与注意事项)
- [7. 片段组件](#7. 片段组件)
-
- [7.1 片段的核心作用](#7.1 片段的核心作用)
- [7.2 片段的定义与使用](#7.2 片段的定义与使用)
-
- [7.2.1 基础用法](#7.2.1 基础用法)
- [7.2.2 在侧边栏或其他容器中使用](#7.2.2 在侧边栏或其他容器中使用)
- [7.3 片段的执行流程](#7.3 片段的执行流程)
- [7.4 片段的关键特性](#7.4 片段的关键特性)
- [7.5 片段与其他功能的对比](#7.5 片段与其他功能的对比)
- [7.6 注意事项与限制](#7.6 注意事项与限制)
- [8. 组件行为(Widget Behavior)](#8. 组件行为(Widget Behavior))
-
- [8.1 组件的核心组成](#8.1 组件的核心组成)
- [8.2 组件身份与唯一标识](#8.2 组件身份与唯一标识)
-
- [8.2.1 基于 Key 的标识(推荐)](#8.2.1 基于 Key 的标识(推荐))
- [8.2.2 基于参数的标识](#8.2.2 基于参数的标识)
- [8.2.3 避免重复组件错误](#8.2.3 避免重复组件错误)
- [8.3 组件交互的执行顺序](#8.3 组件交互的执行顺序)
- [8.4 组件的状态保持与重置](#8.4 组件的状态保持与重置)
- [8.5 组件生命周期与状态持久化](#8.5 组件生命周期与状态持久化)
- [8.6 组件与URL参数绑定(Query Params)](#8.6 组件与URL参数绑定(Query Params))
- [8.7 关键使用注意事项](#8.7 关键使用注意事项)
一、Streamlit 架构与运行机制
1. 运行应用
Streamlit 的运行方式非常简单,只需在普通的 Python 脚本中引入 Streamlit 命令,然后通过以下方式启动即可。
1.1 基础运行命令
最常用、最便捷的启动方式是使用 streamlit run 命令,执行该命令后,Streamlit 会自动启动一个本地服务器,并在默认浏览器中打开你的应用页面,无需额外配置 Web 服务。
bash
streamlit run your_script.py
1.2 向脚本传递自定义参数
如果需要向 Python 脚本传递自定义参数,必须在参数前添加 --,这种方式可以灵活地为应用传递配置参数,方便实现不同场景下的应用配置。
bash
streamlit run your_script.py -- [--script args]
2. 核心架构
Streamlit 应用采用典型的 客户端-服务器(Client-Server) 架构模式,理解这一架构是开发、部署和排错的关键。
2.1 架构概览
- 服务端(Server):即你的 Python 后端程序,由 Streamlit 服务器运行,负责执行代码、处理所有计算、存储数据,并响应客户端请求。
- 客户端(Client):即用户的浏览器,负责展示应用界面、响应用户交互,并将操作事件发送给服务端。
- 本地开发时,服务端和客户端运行在同一台机器上;当应用被网络用户访问时,二者则运行在不同的设备上。
2.2 服务端(Python Backend)
当执行 streamlit run your_app.py 命令时,本地会启动一个 Streamlit 服务器。这个服务器是应用的"大脑":
- 它运行在初始化应用的机器上,所有用户请求的计算任务都由它执行。
- 它为每一个连接的用户维护独立的会话状态,确保不同用户的操作互不干扰。
2.3 客户端(Browser Frontend)
用户通过浏览器访问应用时,其设备就成为了 Streamlit 客户端:
- 它负责渲染由服务端发送的 UI 组件,如文本、图表、按钮等。
- 当用户与界面交互(如点击按钮、输入文本)时,客户端会将这些事件发送给服务端处理。
2.4 架构对应用设计的影响
在开发 Streamlit 应用时,客户端-服务器架构带来了几个重要的设计考量:
- 服务端性能瓶颈:服务端需要处理所有用户的计算和存储请求,其配置必须能应对并发访问的压力。
- 用户文件访问限制 :服务端无法直接访问客户端的本地文件系统,只能处理用户通过
st.file_uploader等组件上传的文件。 - 外设交互限制:若应用需要访问摄像头等外设,必须通过 Streamlit 组件让设备在客户端浏览器中操作,并通过网络将数据传回服务端。
- 进程运行位置:应用中打开浏览器或运行外部程序的代码,都会在服务端执行,而非用户的客户端设备。
- 部署负载均衡限制:在多服务器部署中,若不启用会话亲和性,会导致会话状态丢失、媒体文件加载失败等问题。
2.5 WebSockets 与会话管理
Streamlit 的服务端基于 Tornado 框架构建,核心依赖 WebSockets 协议:
- 它在客户端和服务端之间建立一条持久化的双向通信通道,实现服务端向客户端的实时推送更新。
- 每个浏览器标签页或窗口都会创建独立的 WebSocket 连接,对应服务端的一个独立会话。
2.6 会话亲和性
在大规模部署中,为解决负载均衡导致的会话问题,通常采用以下方案:
- 启用会话亲和性(粘滞会话):确保同一用户的所有请求都被转发到同一个服务器实例上,这是最通用的解决方案。
- 转换媒体文件格式:将图片、音频等媒体文件转换为 Base64 编码的 Data URI,通过 WebSocket 而非 HTTP 传递。
- 使用外部存储:将动态生成的文件保存到 S3 等稳定的外部存储服务中,再将外部 URL 传递给 Streamlit。
3. 应用界面
Streamlit 应用的右上角自带了一套内置交互控件,我们将其统称为 应用界面(The app chrome),包含状态区、工具栏和应用菜单,既能辅助开发者调试,也能为普通用户提供便捷功能。
3.1 应用菜单概览
点击右上角的菜单图标,即可打开应用菜单,菜单分为两部分:
- 通用选项(所有用户可见):包括重新运行、设置、打印、录屏、关于等功能。
- 开发者选项(默认仅本地开发/管理员可见):包括清除缓存、一键部署等功能。

3.2 通用功能详解
-
Rerun(重新运行)
手动触发应用重新运行,不会重置会话状态,
st.session_state和组件值会被保留。快捷键:R(非输入框聚焦时)。 -
Settings(设置)
控制应用运行时的外观与行为:
- 可设置浅色/深色/跟随系统主题,还能自定义主题配色
- 可强制开启宽屏模式,即使脚本未通过
st.set_page_config设置 - 本地开发时,可配置代码修改后的自动刷新行为
-
Print(打印)
调用浏览器的打印功能,支持将应用导出为PDF:
- 可先展开/收起侧边栏,决定是否在打印结果中包含侧边栏
- 深色模式打印时,建议在打印设置中开启「背景图形」
- 可通过调整打印缩放比例,避免元素被截断
-
Record a screencast(录屏)
直接在浏览器内录制应用操作视频,支持同时录制麦克风音频:
- 仅支持 Chrome、Edge、Firefox 等主流浏览器的最新版本
- 录制时标签页会显示红色圆点提示,结束后视频将保存到浏览器下载目录
-
About(关于)
查看当前运行的 Streamlit 版本,开发者可通过
st.set_page_config自定义此处显示的信息。
3.3 开发者选项(Developer options)
默认仅在本地开发或 Streamlit Community Cloud 管理员账号下可见,包含以下核心功能:
- Clear cache(清除缓存) :重置
st.cache_data和st.cache_resource的所有缓存,快捷键:C(非输入框聚焦时) - Deploy this app(一键部署):将本地项目直接部署到 Streamlit Community Cloud,前提是代码已推送到在线 GitHub 仓库
3.4 菜单自定义(Customize the menu)
可通过配置修改菜单的显示行为,核心配置项为 client.toolbarMode,支持以下模式:
| 模式 | 效果 |
|---|---|
auto(默认) |
本地开发/管理员账号下显示开发者选项,其他情况隐藏 |
developer |
向所有用户显示完整开发者选项 |
viewer |
隐藏所有开发者选项,仅显示通用功能 |
minimal |
仅显示外部配置的选项(如通过 st.set_page_config 设置) |
4. 缓存机制
Streamlit 每次交互都会重新运行整个脚本,当涉及数据加载、模型推理等耗时操作时,会导致应用卡顿。缓存机制正是为了解决这一问题,通过存储函数执行结果,避免重复计算,大幅提升应用性能。
4.1 缓存核心装饰器
Streamlit 提供了两个核心缓存装饰器,分别适用于不同场景:
| 装饰器 | 适用场景 | 核心特点 |
|---|---|---|
@st.cache_data |
数据处理、计算类函数 | 缓存返回值(如 DataFrame、列表、数值),会话间共享,可配置过期时间 |
@st.cache_resource |
资源初始化类函数 | 缓存全局资源(如模型、数据库连接),单例模式,跨会话复用 |
4.2 数据结果缓存
@st.cache_data用于缓存函数的返回数据,是数据加载、处理场景的首选。
示例:
python
import streamlit as st
import pandas as pd
# 缓存数据加载函数,避免重复读取文件
@st.cache_data
def load_data(file_path):
df = pd.read_csv(file_path)
return df
# 调用函数,仅首次执行会读取文件
data = load_data("large_data.csv")
st.dataframe(data)
核心特性:
-
参数敏感:函数的参数值变化时,会触发重新计算并更新缓存
-
会话间共享:同一参数的计算结果会被所有用户共享,节省服务器资源
-
可配置过期 :通过
ttl参数设置缓存过期时间(秒),自动刷新数据python# 缓存1小时后自动刷新 @st.cache_data(ttl=3600) def load_database_data(): # 从数据库查询数据 return query_result
4.3 全局资源缓存
@st.cache_resource用于缓存模型、数据库连接等重量级资源,这些资源创建成本高、不适合重复初始化。
示例:
python
import streamlit as st
import torch
# 缓存模型加载,仅在应用启动时加载一次
@st.cache_resource
def load_model():
model = torch.load("my_model.pth")
model.eval()
return model
# 所有用户会话共享同一个模型实例
model = load_model()
核心特性:
-
单例模式:整个应用中仅创建一个实例,跨所有会话复用
-
不复制对象:直接缓存对象本身,不进行序列化/反序列化,适合大型模型
-
适合连接复用 :数据库连接、API客户端等资源可通过该装饰器实现全局复用
python@st.cache_resource def init_db_connection(): conn = psycopg2.connect("postgresql://user:pass@host/db") return conn
4.4 缓存机制的工作原理
- 缓存键生成:Streamlit 会根据函数名、代码哈希、参数值生成唯一的缓存键
- 首次执行:函数首次调用时,执行逻辑并将结果/资源存入缓存
- 后续调用 :再次调用时,检查缓存键是否存在:
- 存在则直接返回缓存结果,跳过执行
- 不存在则重新执行并更新缓存
- 会话隔离与共享 :
cache_data结果会话间共享,cache_resource实例全局单例
4.5 关键使用注意事项
- 缓存函数需纯函数:输入参数相同则输出必须相同,避免包含随机数、时间戳等可变逻辑
- 参数必须可哈希 :列表、字典等不可哈希对象会导致缓存失效,可通过
hash_funcs自定义哈希方式 - 避免副作用:缓存函数内不应包含修改全局变量、发送邮件等外部操作
- 手动清除缓存 :可通过应用菜单的「Clear cache」或快捷键
C清除所有缓存
4.6 自定义缓存行为
-
自定义哈希函数 :处理不可哈希参数
python@st.cache_data(hash_funcs={MyCustomClass: lambda obj: obj.unique_id}) def process_custom_object(obj): # 处理自定义对象 return result -
自定义加载提示 :修改缓存加载时的默认提示文本
python@st.cache_data(show_spinner="正在加载数据,请稍候...") def load_data(): # 耗时操作
5. 会话状态
Streamlit 的脚本在每次用户交互时都会从头重新运行 ,普通变量会被反复重置,无法保存用户的操作状态。会话状态(Session State) 就是为了解决这个问题,它能在整个用户会话期间持久保存变量,让你的应用具备"记忆"能力。
5.1 什么是会话状态
- 会话(Session):用户在浏览器中打开的一个 Streamlit 标签页,就是一个独立的会话。每个会话都是相互隔离的。
- 会话状态(
st.session_state):一个类似 Python 字典的对象,用于在脚本多次重新运行之间,为每个用户会话独立保存和共享变量。 - 它不仅能保存数据,还能与组件(如按钮、滑块)绑定,实现状态联动,甚至支持多页面应用间的数据传递。
5.2 为什么需要会话状态
看一个简单的计数器例子,不使用会话状态时,代码无法正常工作:
python
import streamlit as st
st.title("计数器(错误示例)")
count = 0 # 每次交互都会重置为0
if st.button("点击+1"):
count += 1
st.write(f"当前计数: {count}")

问题分析 :每次点击按钮,脚本都会重新运行,count 会被重置为 0,所以无论点击多少次,计数永远显示为 1。
而使用 st.session_state 后,就能完美解决这个问题;count 的值会被保存在会话状态中,每次点击都会正确累加。
python
import streamlit as st
st.title("计数器(正确示例)")
# 初始化状态变量
if 'count' not in st.session_state:
st.session_state.count = 0
if st.button("点击+1"):
st.session_state.count += 1
st.write(f"当前计数: {st.session_state.count}")

5.3 会话状态的基本用法
st.session_state 的 API 非常直观,支持两种访问方式:字典式和属性式。
-
初始化状态变量
python# 方式1:字典式访问(推荐) if 'key_name' not in st.session_state: st.session_state['key_name'] = "初始值" # 方式2:属性式访问 if 'key_name' not in st.session_state: st.session_state.key_name = "初始值" -
读取和更新状态变量
python# 读取值 st.write(st.session_state['key_name']) st.write(st.session_state.key_name) # 更新值 st.session_state['key_name'] = "新值" st.session_state.key_name = "新值"注意:访问未初始化的状态变量会抛出异常,因此务必先判断变量是否存在再使用。
5.4 回调函数进阶用法
回调函数是在组件状态改变时自动执行的函数,常与会话状态配合使用,实现更复杂的交互逻辑。
-
基础回调示例
pythonimport streamlit as st st.title("计数器(回调示例)") if 'count' not in st.session_state: st.session_state.count = 0 # 定义回调函数 def increment(): st.session_state.count += 1 # 按钮点击时触发回调 st.button("点击+1", on_click=increment) st.write(f"当前计数: {st.session_state.count}")
-
传递参数的回调
通过
args和kwargs可以向回调函数传递参数:pythonimport streamlit as st st.title("带参数的计数器") if 'count' not in st.session_state: st.session_state.count = 0 # 让用户输入步长 step = st.number_input("步长", value=1, step=1) def increment(step_value): st.session_state.count += step_value # 传递参数给回调函数 st.button("增加", on_click=increment, args=(step,)) st.write(f"当前计数: {st.session_state.count}")
5.5 会话状态与组件状态绑定
Streamlit 组件(如滑块、输入框)的值会自动存储在会话状态中 ,通过 key 参数可以直接关联:
python
import streamlit as st
# 创建滑块,并指定key
st.slider("温度(°C)", min_value=-100.0, max_value=100.0, key="celsius")
# 直接通过key获取组件的值
st.write(f"当前温度: {st.session_state.celsius}°C")

重要限制 :无法通过
st.session_state直接修改st.button、st.file_uploader等组件的状态,否则会抛出异常。
5.6 关键注意事项与限制
- 会话生命周期:状态仅存在于当前会话(浏览器标签页)中,关闭标签页或服务器重启后,状态会被清除。
- 可序列化要求:默认情况下,会话状态中的数据会被序列化(Pickle),因此应避免存储不可序列化的对象(如 lambda 函数)。
- 安全风险:Pickle 序列化存在安全隐患,不要加载来自不可信来源的数据。
- 线程安全:每个用户会话是独立的,不同用户的状态不会互相干扰。
6. 表单组件
在 Streamlit 中,普通组件每次值变化都会触发脚本重新运行,而 表单(st.form) 可以将多个输入组件打包,仅在用户点击提交按钮时一次性处理所有输入,避免频繁重运行,提升交互体验和性能。
6.1 表单的核心作用
- 批量提交输入:用户修改表单内的滑块、输入框等组件时,脚本不会立即重运行,仅在提交时统一处理。
- 减少不必要计算:适用于参数较多、计算耗时的场景(如地图配置、数据筛选),避免每改一个参数就触发一次全量重运行。
- 结构化交互:将相关输入组件分组,提升界面的逻辑清晰度。
6.2 表单的基础用法
使用 with st.form("表单名"): 定义表单容器,内部添加输入组件,并通过 st.form_submit_button("提交按钮文字") 创建提交按钮。
示例:地图参数配置表单
python
import streamlit as st
import pandas as pd
import numpy as np
# 生成随机数据的函数
def get_data():
df = pd.DataFrame({
"lat": np.random.randn(200) / 50 + 37.76,
"lon": np.random.randn(200) / 50 + -122.4,
"team": ['A', 'B'] * 100
})
return df
# 生成新数据的按钮(表单外,点击会立即重运行)
if st.button('Generate new points'):
st.session_state.df = get_data()
if 'df' not in st.session_state:
st.session_state.df = get_data()
df = st.session_state.df
# 创建表单
with st.form("map_config_form"):
st.subheader("地图样式配置")
# 分栏布局
color_col, opacity_col, size_col = st.columns(3)
with color_col:
colorA = st.color_picker('Team A 颜色', '#0000FF')
colorB = st.color_picker('Team B 颜色', '#FF0000')
with opacity_col:
opacityA = st.slider('Team A 透明度', 20, 100, 50)
opacityB = st.slider('Team B 透明度', 20, 100, 50)
with size_col:
sizeA = st.slider('Team A 点大小', 50, 200, 100, step=10)
sizeB = st.slider('Team B 点大小', 50, 200, 100, step=10)
# 提交按钮(表单内必须有一个)
submitted = st.form_submit_button("更新地图")
# 提交后更新地图样式
if submitted:
alphaA = int(opacityA * 255 / 100)
alphaB = int(opacityB * 255 / 100)
df['color'] = np.where(df.team == 'A', colorA + f'{alphaA:02x}', colorB + f'{alphaB:02x}')
df['size'] = np.where(df.team == 'A', sizeA, sizeB)
# 显示地图
st.map(df, size='size', color='color')

6.3 表单的交互行为
- 表单内组件:修改表单内的输入框、滑块等组件时,脚本不会重运行,值变化仅保存在前端,直到表单提交才同步到后端。
- 表单外组件:修改表单外的组件(如普通按钮、滑块)会立即触发脚本重运行,表单内未提交的修改不会被保留。
- 提交方式 :
- 点击表单内的
st.form_submit_button按钮提交; - 文本输入框内按
Enter键提交; - 文本域内按
Ctrl+Enter(Mac 为Cmd+Enter)提交。
- 点击表单内的
6.4 表单容器的使用方式
除了 with 语句,还可以直接为表单对象调用方法:
python
import streamlit as st
# 创建表单对象
animal_form = st.form("animal_form")
# 向表单内添加内容
sound = animal_form.selectbox("动物叫声", ['meow', 'woof', 'squeak', 'tweet'])
submit_btn = animal_form.form_submit_button(f"Say it with {sound}!")
user_input = animal_form.text_input("你的句子:", "Where's the tuna?")
# 提交后显示结果
if submit_btn:
animal_form.subheader(f"{user_input.rstrip('.,!?')} {sound}!")
else:
animal_form.subheader("")

6.5 表单提交的处理方式
6.5.1 提交后直接处理(推荐)
在表单外通过判断提交按钮的状态,执行后续逻辑:
python
import streamlit as st
col1, col2 = st.columns(2)
col1.title("加法计算器")
with st.form("addition_form"):
a = st.number_input("输入数字 a")
b = st.number_input("输入数字 b")
submitted = st.form_submit_button("计算")
# 提交后执行计算
if submitted:
col2.title(f"结果: {a + b:.2f}")

6.5.2 配合回调函数处理
通过 on_click 参数为提交按钮绑定回调函数,在提交时执行:
python
import streamlit as st
if 'sum' not in st.session_state:
st.session_state.sum = 0
def calculate_sum():
st.session_state.sum = st.session_state.a + st.session_state.b
col1, col2 = st.columns(2)
col1.title("加法计算器")
with st.form("addition_form"):
st.number_input("输入数字 a", key="a")
st.number_input("输入数字 b", key="b")
st.form_submit_button("计算", on_click=calculate_sum)
col2.title(f"结果: {st.session_state.sum:.2f}")
6.6 表单的关键限制与注意事项
- 必须包含提交按钮 :每个表单必须有且仅有一个
st.form_submit_button,否则无法提交。 - 组件限制 :
st.button不能嵌入表单中;- 在表单中,回调函数仅能绑定到
st.form_submit_button,表单内其他组件不支持回调; - 表单内组件之间无法实时联动(如滑块修改输入框值),仅在提交后才会同步。
- 数据同步 :表单提交前,内部组件的值不会更新到
st.session_state,提交后才会一次性同步。
7. 片段组件
Streamlit 1.37.0 引入的 片段(Fragments) 功能,可以仅重运行应用的一部分代码,而不是整个脚本。这大幅提升了大型复杂应用的性能,让你能更精细地控制应用的执行流程。
7.1 片段的核心作用
片段(Fragments)的核心价值在于局部重运行,仅当用户与片段内的组件交互时,才会重新执行片段内的代码,而不会影响应用的其他部分。它主要适用于以下场景:
- 应用包含多个耗时加载的可视化组件,希望某个筛选器仅更新对应的图表,而不重绘其他组件
- 需要动态更新单个组件或一组组件(如实时数据仪表盘),避免全页重运行的开销
- 表单提交后,无需重运行整个脚本,仅更新需要的结果区域
7.2 片段的定义与使用
通过 @st.fragment 装饰器,可以将任意函数标记为片段函数。当用户与片段内的组件交互时,只会重新执行该片段函数。
7.2.1 基础用法
python
import streamlit as st
# 定义一个片段函数
@st.fragment
def my_fragment():
if st.button("点我(仅重运行此片段)"):
st.success("片段被重新执行了!")
# 调用片段函数
my_fragment()

7.2.2 在侧边栏或其他容器中使用
可以将片段函数放在 st.sidebar、st.columns 等容器中,实现局部重运行:
python
import streamlit as st
@st.fragment
def sidebar_fragment():
st.selectbox("选项", ["A", "B", "C"])
st.slider("数值", 0, 100)
# 将片段放入侧边栏
with st.sidebar:
sidebar_fragment()
7.3 片段的执行流程
当用户与片段内的组件交互时,执行流程如下:
- 片段重运行:仅重新执行片段函数内的代码,应用的其他部分保持不变
- 局部更新:片段内的组件会被重绘,而片段外的组件不会受影响
- 非片段组件交互:如果用户与片段外的组件交互,仍会触发完整的脚本重运行
多片段示例
- 点击片段内的组件:仅该片段重运行,其他部分不变
- 点击主应用中的"更新"按钮:触发完整脚本重运行,所有组件都会重绘
python
import streamlit as st
st.title("片段执行演示")
# 片段1:切换按钮和文本区域
@st.fragment
def toggle_and_text():
col1, col2 = st.columns(2)
col1.toggle("切换开关")
col2.text_area("输入文本")
# 片段2:筛选和文件上传
@st.fragment
def filter_and_upload():
col1, col2 = st.columns(2)
col1.checkbox("启用筛选")
col2.file_uploader("上传图片")
# 主应用代码
toggle_and_text()
col1, col2 = st.columns(2)
col1.selectbox("选择", [1, 2, 3])
col2.button("更新(触发全页重运行)")
filter_and_upload()

7.4 片段的关键特性
-
与会话状态交互 :片段函数可以读写
st.session_state,这是片段与应用其他部分共享数据的推荐方式 -
自动重运行(流模式) :通过
run_every参数,可以让片段按指定时间间隔自动重运行,无需用户交互python@st.fragment(run_every="10s") def auto_update(): df = get_latest_data() # 获取最新数据 st.line_chart(df) # 每10秒自动更新图表 -
从片段内触发全页重运行 :在片段函数内调用
st.rerun(),可以触发整个脚本重运行
7.5 片段与其他功能的对比
| 功能 | 核心区别 | 适用场景 |
|---|---|---|
| 片段(Fragments) | 局部重运行,交互时立即处理,可实时更新 | 单组件更新、实时数据、局部筛选 |
| 表单(Forms) | 批量提交,仅提交时处理,无实时更新 | 多参数配置、批量提交场景 |
| 回调(Callbacks) | 脚本重运行前执行,无法控制重运行范围 | 简单交互逻辑、状态同步 |
| 缓存(Caching) | 跳过重复计算,不改变重运行范围 | 数据加载、模型推理等耗时操作 |
7.6 注意事项与限制
- 返回值不推荐 :Streamlit 在片段重运行时会忽略函数返回值,建议通过
st.session_state共享数据 - 容器行为差异 :
- 片段主容器内的元素:重运行时会被清除并重绘
- 片段外的容器元素:会累积显示,直到下一次全页重运行,可使用
st.empty()避免元素累积
- 输入输出限制 :片段函数无法直接检测输入值的变化,建议使用
st.session_state管理状态 - 嵌套限制:片段函数不支持嵌套使用,且片段内无法定义新的片段函数
8. 组件行为(Widget Behavior)
组件(如 st.button、st.selectbox、st.text_input)是 Streamlit 应用的核心交互元素,它们将用户输入传递给 Python 代码。理解组件的工作原理、生命周期和关键行为,是避免异常、实现复杂交互的基础。
8.1 组件的核心组成
每个 Streamlit 组件都由四部分构成:
- 前端界面:用户在浏览器中看到的交互控件(如按钮、输入框)
- 后端状态:Python 内存中维护的组件状态
- 会话状态映射 :
st.session_state中与组件值关联的键值对 - 返回值 :组件函数执行时返回的当前值(如
st.button返回布尔值)
8.2 组件身份与唯一标识
Streamlit 依靠组件身份(Widget Identity)来识别和维护组件状态,分为两种模式:
8.2.1 基于 Key 的标识(推荐)
当组件指定了 key 参数时,key 就是组件身份的主要决定因素。
- 优势:修改标签、占位符等参数时,组件状态不会被重置
- 例外 :修改
min_value/max_value(如滑块)或options(如下拉框)等约束性参数时,即使指定了key,组件仍可能被重置
8.2.2 基于参数的标识
未指定 key 时,组件身份由其参数(label、options、min/max 等)共同决定。
- 问题 :任何参数变化(如修改
label文本)都会被 Streamlit 视为新组件,导致状态重置
8.2.3 避免重复组件错误
同一页面上,两个同类型组件若参数完全相同(且未指定 key),会抛出 DuplicateWidgetID 错误。解决方法是为它们指定不同的 key。
python
# ❌ 错误示例:两个相同按钮,无唯一标识
st.button("OK")
st.button("OK")
# ✅ 正确示例:指定唯一 key
st.button("OK", key="privacy_btn")
st.button("OK", key="terms_btn")
8.3 组件交互的执行顺序
当用户与组件交互时,Streamlit 会按以下顺序执行:
- 更新
st.session_state中对应的组件值 - 执行组件的回调函数(
on_change/on_click) - 重新运行整个脚本,组件函数返回更新后的值
注意:回调函数在脚本重运行前执行,因此回调中不应调用其他组件函数,否则内容会在下次重运行时消失。
8.4 组件的状态保持与重置
组件的状态(用户输入的值)能否被保留,取决于其身份是否保持不变:
- 身份不变:状态会被保留,用户输入的值会被记住
- 身份变化:Streamlit 会将其视为新组件,状态会被重置为默认值
场景示例:动态参数的滑块
当滑块的 min/max 值由其他输入控制时,修改 min/max 会导致滑块身份变化,状态重置。
python
import streamlit as st
col1, col2 = st.columns(2)
max_val = col1.number_input("最大值", 1, 10, 7)
min_val = col2.number_input("最小值", 1, 7, 5)
# ❌ 无 key,修改 min/max 会重置状态
st.slider("无 key 滑块", min_val, max_val, value=7)
# ✅ 有 key,配合会话状态维护值,避免重置
if "slider_val" not in st.session_state:
st.session_state.slider_val = 7
def update_slider():
# 确保值在新的 min/max 范围内
st.session_state.slider_val = max(min(st.session_state.slider_val, max_val), min_val)
st.slider("有 key 滑块", min_val, max_val, key="slider_val", on_change=update_slider)
8.5 组件生命周期与状态持久化
-
会话级持久化:组件状态仅存在于当前浏览器标签页会话中,关闭标签页或切换页面后,未被会话状态显式保存的状态会被清除
-
跨页面状态保存 :若需要在页面间保留组件值,需将其显式复制到独立的会话状态变量中
python# 保存组件值到独立会话状态变量,跨页面保留 st.number_input("输入值", key="temp_val", on_change=lambda: st.session_state.update({"persist_val": st.session_state.temp_val})) -
清理机制:脚本运行结束或页面切换时,Streamlit 会清理当前页面未显示组件的状态
8.6 组件与URL参数绑定(Query Params)
Streamlit 1.55.0 及以上版本支持通过 bind="query-params" 参数,将组件值同步到 URL 查询参数中:
python
import streamlit as st
# 将下拉框的值同步到 URL 的 color 参数
st.selectbox("颜色选择", ["Red", "Green", "Blue"], key="color", bind="query-params")
- 效果 :选择 "Green" 后,URL 会变为
?color=Green,用户可分享、收藏该链接,直接打开即可恢复之前的选择 - 特性:默认值对应的参数不会出现在 URL 中,保持链接简洁;无效值会被忽略
8.7 关键使用注意事项
- 多页面应用 :使用
st.navigation时,公共组件应放在入口文件中,避免页面切换时的状态问题 - 回调与组件值 :回调函数中直接访问组件值时,应通过
st.session_state获取,而非传递参数 - 隐藏组件:临时隐藏组件会导致其状态被重置,如需保留状态,应使用会话状态显式存储