概述
本文搜集几个流行的后端友好、基于Python语言、交互式Web框架,可实现无需过多前端开发技能,几行代码实现一个MVP展示界面,可应用:数据可视化、原型快速开发、机器学习数据探索等诸多场景。
Streamlit
官网,开源(GitHub,41.4K Star,3.7K Fork),Python Web框架,可用极少代码快速搭建交互式Web数据应用,如数据分析、可视化展示等。提供在线体验地址Playground。
功能
- 轻松创建Web应用:简化将Python脚本转为响应式网页的过程,只需几行代码,无需Flask、Django或前端经验
- 数据处理与EDA:使用pandas和numpy等库导入和处理数据,进行探索性数据分析(EDA)以挖掘洞见
- 动态可视化:内置图表能展示趋势和比较,配合交互式控件和美观的图表
- 直观的用户控制:下拉菜单、滑块和侧边栏让用户能引导分析,为仪表板带来交互性
特点:
- 不需要开发者拥有丰富的前端知识;
- 包含丰富的数据展示形式;
- 通过Python代码可控制应用程序的运行逻辑。
架构图
3个模块:
- 数据展示;
- 数据输入;
- 流程和UI设置。
使用
安装:pip install streamlit。
使用命令行工具,需配置环境变量。
importstreamlitasstimportpandasaspdimportnumpyasnpimportmatplotlib.pyplotasplt st.title('标题')st.header('')st.subheader('')st.text('')df=pd.DataFrame({'列1':[1,2,3],'列2':[4,5,6]})st.dataframe(df)x=np.linspace(0,10,100)y=np.sin(x)plt.plot(x,y)st.pyplot(plt)name=st.text_input('请输入名字')st.write(name)ifst.button('点击')st.write('好的')输入streamlit run demo.py命令,浏览器打开http://localhost:8501体验吧。
部署需要到官网与GitHub账户进行认证,并在官网上完成部署。
session_state:
问题:
- 异步函数适合非阻塞代码,但Streamlit的主循环是同步的;
- 顶级 Streamlit 代码中不允许使用原生的 await。
快速解决方案:
- 如果你不在异步循环中,使用Python的
asyncio.run()。 - 如果在 Jupyter 中遇到循环错误,使用
nest_asyncio.apply()修补循环。
进阶技巧:
- 使用
st.container()或st.expander()进行视觉分组; - 标题和图标(如 📝 或 🔗)打破单调,引导视线;
- Markdown支持允许引入丰富格式、链接、LaTeX方程;
- 使用
st.sidebar添加设置(比如选择结果数量,切换摘要或完整答案); - 通过
st.markdown和unsafe_allow_html=True添加自定义CSS,打造主题背景和卡片; - 使用
st.error或st.warning优雅地显示错误信息。
控件
包括:
- radio:单选按钮
- checkbox:多选框
- selectbox:下拉框
- multiselect:下拉多选框
- button:按钮
- download_button:下载
- slider:滑块
- select_slider:选择条
- text_input:单行文本输入
- text_area:多行文本输入
- number_input:数值输入
- date_input:日期输入
- time_input:时间输入
- file_uploader:文件上传
- camera_input:摄像头拍照
- color_picker:颜色选择
- image:展示图片
- audio:展示音频
- video:展示视频
Gradio
官网,开源(GitHub,40K Star,3.1K Fork)。理念:为Python函数提供最快捷的路径,使其成为一个可分享、可交互的Web演示应用,尤其是在机器学习领域。
特点:
- 代码极简:3行代码搭出MVP界面;
- 实时更新:改代码不用重启服务,刷新网页就能看效果;
- 多模态交互:支持文字、图片、音频、视频输入和输出;
- 多应用场景:快速演示AI模型效果、展示项目、收集用户反馈、教学模型可视化;
- 支持团队协作:多人同时编辑界面,实时同步修改记录。
多模态交互
| 输入类型 | 输出类型 | 典型应用 |
|---|---|---|
| 图像/视频 | 标签/文本/图像 | 图像分类、目标检测、风格迁移 |
| 文本 | 文本/代码/表格 | 问答系统、代码生成、数据清洗 |
| 音频 | 文本/波形图 | 语音转写、情感分析、频谱可视化 |
| 文件/数据集 | 可视化图表 | CSV分析、PDF摘要、数据探索 |
应用场景
- 学术研究:快速验证论文算法,论文复现,生成可演示Demo
- 企业服务:将内部模型封装为API测试工具,供非技术人员使用
- 教育培训:创建交互式教学模块
- 产品原型:用MVP界面争取投资,演示AI功能而非纯技术方案
局限与解决方案
- 局限性:
- 复杂布局需使用
gr.Blocks(),灵活性低于Dash - 缺乏企业级权限管理功能
- 复杂布局需使用
- 解决方案:
- 结合
Flask扩展:通过gr.Interface.launch(share=False)导出API,再用Flask封装 - 接入
Okta认证:通过反向代理实现企业级权限控制
- 结合
实战
安装:pip install gradio
importgradioasgrdefgreet(name,intensity):return"Hello, "+name+"!"*int(intensity)# 创建界面demo=gr.Interface(fn=greet,# 函数inputs=["text","slider"],# 对应函数的输入参数outputs=["text"],# 对应函数的返回值)demo.launch(share=True)解读:Gradio的核心,即Interface类。提供函数、输入、输出,Gradio会自动处理剩下的所有事情:UI布局、组件渲染和数据流转。
gr.Interface更多参数:
- live:默认False,设置为True,实时处理
- title:标题,str格式
- description:描述,str格式
通过share参数分享demo.launch(share=True),公网链接有效期1周。
遇到的问题,在Windows下通过Git Bash终端执行命令python gradio_demo.py,命令行没有任何输出:
换成CMD终端,输出正常:
Dash
官网,Plotly推出、基于Python的开源(GitHub,24.1K Star,2.2K Fork)框架,用于快速构建数据驱动的Web应用程序。
结合Flask的后端能力、Plotly.js(支持多种图表类型,如折线图、柱状图、散点图、热力图等)的可视化能力以及React.js的交互能力,允许用户通过简单的Python代码创建交互式的数据可视化应用,而无需掌握复杂的前端技术。特别适合数据科学家、分析师和工程师,展示数据分析结果、机器学习模型预测或其他数据驱动的功能。
原理
Dash应用由两个主要部分组成:布局(Layout)和交互性(Interactivity)。
布局
导入核心组件:from dash import Dash, html, dcc。布局定义应用程序的外观和结构,通过代码来描述的,使用dash_html_components和dash_core_components两个库来创建HTML元素和交互式组件。Dash 2.0版本开始,这两者已被整合到dash主包中。
dash_html_components:提供与HTML标签对应的Python类。如,html.Div对应HTML中的<div>标签。通过这些组件,可轻松地构建页面结构/元素。dash_core_components:提供更高级交互式组件,如滑块、下拉菜单、图形等。
常用交互组件
| 组件 | 说明 | 示例代码 |
|---|---|---|
dcc.Input | 创建一个文本输入框 | dcc.Input(id='input',type='text',placeholder='请输入内容...') |
dcc.Dropdown | 创建一个下拉菜单 | dcc.Dropdown(id='dropdown',options=[{'label':'选项1','value':'1'}],value='1') |
dcc.Slider | 创建一个滑块 | dcc.Slider(id='slider',min=0,max=10,step=1,value=5) |
dcc.Graph | 创建一个交互式图表(基于Plotly.js) | dcc.Graph(id='graph',figure={'data':[{'x':[1,2,3],'y':[4,1,2],'type':'bar'}]}) |
dcc.Textarea | 创建一个多行文本输入框 | dcc.Textarea(id='textarea',value='请输入多行文本...') |
dcc.Checklist | 创建一个复选框列表 | dcc.Checklist(id='checklist',options=[{'label':'选项1','value':'1'}],value=['1']) |
dcc.RadioItems | 创建一个单选按钮组 | dcc.RadioItems(id='radio',options=[{'label':'选项1','value':'1'}],value='1') |
dcc.DatePickerSingle | 创建一个日期选择器(单选) | dcc.DatePickerSingle(id='date-picker',date='2023-10-01') |
dcc.DatePickerRange | 创建一个日期范围选择器 | dcc.DatePickerRange(id='date-range',start_date='2023-10-01',end_date='2023-10-07') |
dcc.Markdown | 渲染Markdown文本 | dcc.Markdown('''#标题\n-列表项1''') |
dcc.Store | 在客户端存储数据,用于跨回调共享数据 | dcc.Store(id='store',data={'key':'value'}) |
dcc.Upload | 创建一个文件上传组件 | dcc.Upload(id='upload',children=html.Div('拖放或点击上传文件')) |
dcc.Tabs | 创建选项卡组件 | dcc.Tabs(id='tabs',children=[dcc.Tab(label='标签1',value='1')]) |
dcc.Tab | 创建单个选项卡(需与dcc.Tabs配合使用) | dcc.Tab(label='标签1',value='1') |
dcc.Interval | 定时触发回调的组件 | dcc.Interval(id='interval',interval=1000) |
dcc.Location | 用于管理URL的组件 | dcc.Location(id='url',pathname='/') |
dcc.Link | 创建一个超链接,用于页面导航(需与dcc.Location配合使用) | dcc.Link('跳转到首页',href='/') |
dcc.ConfirmDialog | 创建一个确认对话框 | dcc.ConfirmDialog(id='confirm',message='确定要执行此操作吗?') |
dcc.ConfirmDialogProvider | 提供一个确认对话框(需与按钮等组件配合使用) | dcc.ConfirmDialogProvider(html.Button('删除'),id='confirm-provider') |
dcc.Loading | 创建一个加载动画组件 | dcc.Loading(id='loading',children=[html.Div('加载中...')]) |
dcc.Download | 用于触发文件下载的组件 | dcc.Download(id='download') |
交互性
通过回调函数(Callback)来实现的。回调函数允许你在用户与应用程序交互时动态更新页面内容。例如,当用户选择一个下拉菜单选项时,图表可以自动更新以显示相应的数据。
回调函数通过@app.callback装饰器来定义,并指定输入和输出。输入通常是用户交互的组件(如滑块、下拉菜单等),而输出则是需要更新的组件(如图表、文本等)。
回调函数
一个典型的Dash回调函数包含以下几个部分:
- 输入(Input):指定哪些组件的属性变化会触发回调函数
- 输出(Output):指定回调函数执行后,哪些组件的属性会被更新
- 状态(State):可选参数,用于传递一些不会触发回调但需要在回调中使用的数据
- 回调函数体:包含实际的逻辑代码,用于处理输入并生成输出
工作原理
- 输入与输出的绑定
输入和输出是通过Input和Output对象来指定的。Input对象指定哪些组件的哪些属性变化会触发回调函数,而Output对象指定回调函数执行后,哪些组件的哪些属性会被更新。 - 回调函数的触发
当用户在界面上进行操作(输入文本、点击按钮等)时,相关的组件属性会发生变化。Dash会检测到这些变化,并自动调用与之绑定的回调函数。 - 回调函数的执行
回调函数执行时,Dash会将输入属性的当前值作为参数传递给回调函数。回调函数根据这些输入值进行计算或处理,并返回输出属性的新值。Dash会自动将返回的值更新到指定的组件属性中。
高级用法
- 多个输入与输出:
一个回调函数可以有多个输入和输出:
@app.callback([Output('output-div-1','children'),Output('output-div-2','children')],[Input('input-1','value'),Input('input-2','value')])defupdate_outputs(input1,input2):returnf'Input 1:{input1}',f'Input 2:{input2}'回调函数update_outputs有两个输入和两个输出。当input-1或input-2的值发生变化时,回调函数会被触发,并更新两个输出组件的children属性。
- 使用状态(State)
有时候,可能需要在回调函数中使用一些不会触发回调的数据。这时可使用State对象。State对象与Input对象类似,但它不会触发回调函数。
@app.callback(Output('output-div','children'),[Input('submit-button','n_clicks')],[State('input-text','value')])defupdate_output(n_clicks,input_value):ifn_clicksisNone:return'No clicks yet'returnf'Button clicked{n_clicks}times. Input:{input_value}'n_clicks是触发回调的输入,而input_value是回调函数中使用的状态。只有当按钮被点击时,回调函数才会被触发,但回调函数中可以使用输入框的当前值。
- 防止回调函数重复执行
在某些情况下,回调函数可能会被频繁触发,导致性能问题。为避免这种情况,可使用dash.no_update来防止不必要的更新。
@app.callback(Output('output-div','children'),[Input('input-text','value')])defupdate_output(input_value):ifnotinput_value:returndash.no_updatereturnf'You have entered:{input_value}'如果输入框的值为空,回调函数将不会更新输出组件。
如果回调函数执行缓慢,可考虑以下优化方法:
- 减少回调函数的计算量:尽量避免在回调函数中进行复杂的计算
- 使用缓存:对于重复的计算结果,可以使用缓存来减少计算时间
- 异步回调:对于长时间运行的任务,可以使用异步回调来避免阻塞主线程
实战
安装:pip install dash
示例
fromdashimportDash,dcc,html,Input,Output app=Dash(__name__)app.layout=html.Div([dcc.Input(id='input',# 输入框ID,用于回调函数value='初始值',type='text'#),# 用于显示输出html.Div(id='output')])# 定义回调函数@app.callback(Output('output','children'),# 输出到id为'output'的Div的children属性Input('input','value')# 输入来自id为'input'的输入框的value属性)defupdate_output_div(input_value):returnf'你输入了:{input_value}'if__name__=='__main__':app.run_server(debug=True)运行程序,浏览器打开http://localhost:8050
在Jupyter Notebook中使用Dash,需安装jupyter-dash:pip install jupyter-dash。
Dash Bootstrap Components提供Bootstrap风格的组件,用于快速构建美观的布局,安装:pip install dash-bootstrap-components,引入import dash_bootstrap_components as dbc。
示例
fromdashimportDash,htmlimportdash_bootstrap_componentsasdbc app=Dash(__name__,external_stylesheets=[dbc.themes.BOOTSTRAP])app.layout=dbc.Container(children=[dbc.Row(dbc.Col(html.H1("欢迎使用Dash Bootstrap",className="text-center text-primary"))),dbc.Row(dbc.Col(dbc.Card(children=[dbc.CardHeader("卡片标题"),dbc.CardBody(children=[html.P("Bootstrap风格卡片",className="card-text")])],className="mt-4"# 设置外边距)))])if__name__=='__main__':app.run_server(debug=True)Dash DAQ是一个用于创建数据采集和控制应用的组件库,提供丰富的样式组件,安装pip install dash-daq。
示例:
fromdashimportDash,htmlimportdash_daqasdaq app=Dash(__name__)app.layout=html.Div(style={'textAlign':'center'},children=[daq.Thermometer(id='thermometer',value=25,min=0,max=100,label="温度计",style={'margin':'20px'}),daq.Gauge(id='gauge',value=50,min=0,max=100,label="压力表",style={'margin':'20px'})])if__name__=='__main__':app.run_server(debug=True)多页面布局
在现代Web应用开发中,单页面应用(SPA)和多页面应用(MPA)是两种常见的架构模式。
单页面应用通常通过动态加载内容来提供流畅的用户体验,而多页面应用则通过多个独立的页面来组织内容。
在单页面应用中,所有内容都在一个页面中动态加载和更新,用户通过点击链接或按钮来切换不同的视图。优点是用户体验流畅,页面加载速度快;缺点是应用规模增大后,代码结构变得复杂。
多页面应用则通过多个独立的页面来组织内容,每个页面都有自己的URL和布局。优点是代码结构清晰,易于维护,缺点是页面切换时会有一定的加载延迟。
可通过dcc.Location和dcc.Link组件来实现多页面布局,前者用于跟踪当前页面的URL,后者用于在页面之间导航。结合回调函数,可根据URL动态加载不同的页面内容。
Plotly Studio
官方提供的安装程序,支持Windows、MacOS,理念:Vibe Analytics,氛围分析。
Hyperdiv
官网,开源(GitHub,934 Star,31 Fork)。用Python快速搞定浏览器端交互UI的神器。即时模式(immediate‐mode)语法,几行代码就能渲染组件。内置Shoelace组件库、Markdown渲染支持、Chart.js图表、浏览器本地存储读写,以及纯Python表单校验。
| 功能/环节 | 传统前端 | Hyperdiv |
|---|---|---|
| 组件库 | 手动安装、导入 | 内置Shoelace,一行引入 |
| 状态/渲染逻辑 | JS、异步/钩子 | Python立即模式,函数里写就行 |
| Markdown支持 | 单独库+配置 | 内置Mistune,一行调用 |
| 图表展示 | Chart.js+对接代码 | 内置Chart.js,API直观 |
| 本地存储 | localStorageAPI | Pythonread/write封装好 |
| 表单校验 | JS骚操作+验证库 | Python+内置校验,逻辑都在一个地方 |
安装:pip install hyperdiv
示例:
fromhyperdivimportapp,html,storage,form,chart# 表单classUserForm(form.Model):username=form.TextField(min_length=3)age=form.NumberField(min_value=1)@app.route("/")defindex():# 从本地存储读上一次输入data=storage.load("user_info")or{}form_ui=UserForm(data)# 表单提交回调@form_ui.on_submitdefhandle_submit(clean_data):storage.save("user_info",clean_data)chart_data={"labels":["Age"],"datasets":[{"data":[clean_data["age"]]}]}returnhtml.div([html.h3(f"你好,{clean_data['username']}!"),chart.Bar(data=chart_data)])# 页面布局returnhtml.div([html.h1("Hyperdiv 表单 + 图表 Demo"),form_ui.render()])| 优点 | 缺点 |
|---|---|
| Python全栈一致,减少语言切换成本 | 目前生态还小,组件或插件有限 |
| 即时模式写法清爽 | 对超大规模复杂应用支持尚需验证 |
| 内置常用组件:Shoelace、Markdown、Chart | 浏览器兼容性待更多平台测试 |
| 阅读/写入本地存储、表单校验一站式搞定 | 文档、教程相对少,新人上手有点陡峭 |
| 零配置启动,Demo看完就能上手 | 高度定制化时可能需要写少量JS |
对比
仅供参考
| 维度 | Gradio | Streamlit | Dash |
|---|---|---|---|
| 开发效率 | 3行代码生成完整界面 | 需10+行基础布局代码 | 需20+行配置式代码 |
| 交互体验 | 支持流式传输(如视频逐帧预览) | 仅支持页面刷新 | 依赖回调函数,响应较慢 |
| 组件丰富度 | 20+专用AI组件(画布、3D模型查看器) | 15+通用组件(缺少AI专项优化) | 依赖第三方插件扩展 |
| 部署便捷性 | 原生支持HuggingFace托管 | 需手动配置Nginx反向代理 | 需Docker容器化部署 |
| 学习曲线 | 1小时可上手 | 3小时掌握基础功能 | 需较长时间学习 |
总结:
- AI场景专精:Gradio的
gr.Image(type="pil")、gr.Audio(source="microphone")等组件深度适配ML需求,而Streamlit更侧重数据可视化,如数据密集型的仪表盘或数据分析 - 性能优势:Gradio的增量更新技术(仅刷新变化区域)比Streamlit全页面刷新快3倍以上
- 生态壁垒:Dash依赖Plotly生态,Gradio直接集成PyTorch/TensorFlow,更适合AI开发者
参考
- 大模型导论
- 菜鸟教程