Advanced concepts of Streamlit

既然你已经了解了Streamlit应用程序如何运行和处理数据,现在让我们来谈谈如何提高效率。缓存允许你保存函数的输出,以便在重新运行时可以跳过它。会话状态允许你为每个用户保存信息,这些信息在重新运行之间保持不变。这不仅可以帮助你避免不必要的重新计算,还可以让你创建动态页面并处理渐进式过程。

缓存可以让您的应用程序在从网络加载数据、操作大型数据集或执行昂贵的计算时保持高性能。

缓存的基本思想是存储昂贵函数调用的结果,并在相同的输入再次出现时返回缓存的结果。这避免了使用相同输入值重复执行函数。

要在 Streamlit 中缓存一个函数,你需要为其应用一个缓存装饰器。你有两种选择:

  • st.cache_data 是推荐用于缓存返回数据的计算的方式。当你使用一个返回可序列化数据对象(例如 str、int、float、DataFrame、dict、list)的函数时,使用 st.cache_data它在每次函数调用时创建数据的新副本,使其免受突变和竞争条件的影响。st.cache_data 的行为在大多数情况下是你想要的——所以如果你不确定,从 st.cache_data 开始,看看它是否有效!
  • st.cache_resource 是推荐用于缓存全局资源(如机器学习模型或数据库连接)的方式。当你的函数返回不可序列化的对象且你不想多次加载时,使用 st.cache_resource它返回缓存的对象本身,该对象在所有重新运行和会话之间共享,无需复制或重复。如果你修改了使用 st.cache_resource 缓存的对象,该修改将在所有重新运行和会话中存在。

示例:

@st.cache_data def long_running_function(param1, param2): return …

在上面的例子中,long_running_function@st.cache_data 装饰。因此,Streamlit 记录了以下内容:

  • 函数的名称 ("long_running_function")。
  • 输入的值(param1, param2)。
  • 函数内的代码。

在运行long_running_function中的代码之前,Streamlit会检查其缓存中是否有之前保存的结果。如果它找到给定函数和输入值的缓存结果,它将返回该缓存结果,而不会重新运行函数的代码。否则,Streamlit会执行该函数,将结果保存在其缓存中,并继续脚本的运行。在开发过程中,缓存会随着函数代码的变化自动更新,确保缓存中反映最新的更改。

Streamlit's two caching decorators and their use cases. Use st.cache_data for anything you'd store in a database. Use st.cache_resource for anything you can't store in a database, like a connection to a database or a machine learning model.

Streamlit的两个缓存装饰器及其使用案例。

有关Streamlit缓存装饰器、其配置参数及其限制的更多信息,请参阅Caching

会话状态提供了一个类似字典的接口,您可以在其中保存脚本重新运行之间保留的信息。使用st.session_state与键或属性表示法来存储和调用值。例如,st.session_state["my_key"]st.session_state.my_key。请记住,小部件会自行处理其状态,因此您并不总是需要使用会话状态!

会话是查看应用程序的单个实例。如果您从浏览器的两个不同标签页查看应用程序,每个标签页将拥有自己的会话。因此,应用程序的每个查看者都将有一个与其特定视图相关联的会话状态。当用户与应用程序交互时,Streamlit 会维护此会话。如果用户刷新浏览器页面或重新加载应用程序的URL,他们的会话状态将重置,并重新开始一个新的会话。

这是一个简单的应用程序,用于计算页面运行的次数。每次点击按钮时,脚本将重新运行。

import streamlit as st if "counter" not in st.session_state: st.session_state.counter = 0 st.session_state.counter += 1 st.header(f"This page has run {st.session_state.counter} times.") st.button("Run it again")
  • 首次运行: 应用程序首次为每个用户运行时,会话状态为空。因此,创建了一个键值对("counter":0)。随着脚本的继续,计数器立即增加("counter":1),并显示结果:“此页面已运行1次。”当页面完全渲染后,脚本完成,Streamlit服务器等待用户进行操作。当用户点击按钮时,重新运行开始。

  • 第二次运行: 由于 "counter" 已经是会话状态中的一个键,它不会被重新初始化。随着脚本的继续执行,计数器增加("counter":2),结果显示为:"此页面已运行 2 次。"

有几个常见的场景中,会话状态(Session State)非常有用。如上所示,当您有一个逐步进行的过程,并希望在一次重新运行到下一次之间建立起来时,会话状态就会被使用。会话状态也可以用于防止重新计算,类似于缓存。然而,它们之间的差异很重要:

  • 缓存将存储的值与特定的函数和输入关联起来。所有用户在所有会话中都可以访问缓存的值。
  • 会话状态将存储的值与键(字符串)关联。会话状态中的值仅在保存它的单个会话中可用。

如果你的应用程序中有随机数生成功能,你可能会使用会话状态。这里有一个例子,数据在每个会话开始时随机生成。通过将这些随机信息保存在会话状态中,每个用户打开应用程序时会得到不同的随机数据,但在他们与应用程序交互时,数据不会不断变化。如果你使用选择器选择不同的颜色,你会看到数据不会在每次重新运行时重新随机化。(如果你在新标签页中打开应用程序以开始新会话,你会看到不同的数据!)

import streamlit as st import pandas as pd import numpy as np if "df" not in st.session_state: st.session_state.df = pd.DataFrame(np.random.randn(20, 2), columns=["x", "y"]) st.header("Choose a datapoint color") color = st.color_picker("Color", "#FF0000") st.divider() st.scatter_chart(st.session_state.df, x="x", y="y", color=color)

如果您为所有用户提取相同的数据,您可能会缓存一个检索该数据的函数。另一方面,如果您提取特定于用户的数据,例如查询他们的个人信息,您可能希望将其保存在会话状态中。这样,查询的数据仅在该会话中可用。

正如基本概念中提到的,会话状态也与小部件相关。小部件非常神奇,它们会默默地处理状态。然而,作为一个高级功能,您可以通过为小部件分配键来在代码中操作它们的值。分配给小部件的任何键都会成为会话状态中的一个键,该键与小部件的值相关联。这可以用来操作小部件。在您理解Streamlit的基础知识后,如果您感兴趣,可以查看我们的小部件行为指南以深入了解细节。

如上所述,你可以使用@st.cache_resource来缓存连接。这是最通用的解决方案,允许你使用几乎任何Python库中的连接。然而,Streamlit还提供了一种便捷的方式来处理一些最流行的连接,比如SQL!st.connection为你处理了缓存,因此你可以享受更少的代码行数。从数据库中获取数据可以像这样简单:

import streamlit as st conn = st.connection("my_database") df = conn.query("select * from my_table") st.dataframe(df)

当然,您可能想知道您的用户名和密码放在哪里。Streamlit 提供了一个方便的机制用于Secrets management。现在,让我们看看st.connection如何很好地与secrets配合使用。在您的本地项目目录中,您可以保存一个.streamlit/secrets.toml文件。您可以将您的secrets保存在toml文件中,st.connection就会使用它们!例如,如果您有一个应用文件streamlit_app.py,您的项目目录可能看起来像这样:

your-LOCAL-repository/ ├── .streamlit/ │ └── secrets.toml # Make sure to gitignore this! └── streamlit_app.py

对于上面的SQL示例,你的secrets.toml文件可能如下所示:

[connections.my_database] type="sql" dialect="mysql" username="xxx" password="xxx" host="example.com" # IP or URL port=3306 # Port number database="mydb" # Database name

由于您不希望将secrets.toml文件提交到您的存储库中,因此在准备发布应用程序时,您需要了解您的主机如何处理机密信息。每个主机平台可能有不同的方式来传递您的机密信息。例如,如果您使用Streamlit Community Cloud,每个部署的应用程序都有一个设置菜单,您可以在其中加载您的机密信息。在编写完应用程序并准备部署后,您可以阅读有关如何在Community Cloud上部署您的应用程序的所有信息。

forum

还有问题吗?

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