Dash 是一个用于构建基于 Web 的应用程序的 Python 库,无需 JavaScript 。
Dash 用于创建分析 Web 应用程序的用户界面库。那些使用 Python 进行数据分析、数据挖掘、可视化、建模、仪器控制和报告的人可以立即使用 Dash 。
它建立在 Plotly.js、React 和 Flask 之上,将现代 UI 元素(如下拉列表、滑块和图形)与你的分析 Python 代码相结合。
就在昨晚,Dash
框架发布了其2.9.0版本更新,在一众更新内容中,有两条新特性在我看来**「尤为重要」**,可以大幅度提升我们开发Dash
应用的效率,下面我就将带大家一起了解它们的具体内容:
技术要学会分享、交流,不建议闭门造车。一个人可以走的很快、一堆人可以走的更远。
本文由技术群粉丝分享,项目源码、数据、技术交流提升,均可加交流群获取,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友
方式①、添加微信号:pythoner666,备注:来自CSDN
方式②、微信搜索公众号:Python学习与数据挖掘,后台回复:加群
在之前版本的Dash
中,严格限制了不同的回调函数不可以对相同的id.属性
目标进行输出,以下面的示例应用为例:
import dash
from dash import html
import feffery_antd_components as fac
from dash.dependencies import Input, Output
app = dash.Dash(__name__)
app.layout = html.Div(
[
fac.AntdSpace(
[
fac.AntdButton(
'按钮1',
id='button-demo1'
),
fac.AntdButton(
'按钮2',
id='button-demo2'
)
]
),
fac.AntdParagraph(
id='output-demo'
)
],
style={
'padding': '50px 100px'
}
)
@app.callback(
Output('output-demo', 'children'),
Input('button-demo1', 'nClicks'),
prevent_initial_call=True
)
def trigger1(nClicks):
return f'按钮1: {nClicks}'
@app.callback(
Output('output-demo', 'children'),
Input('button-demo2', 'nClicks'),
prevent_initial_call=True
)
def trigger2(nClicks):
return f'按钮2: {nClicks}'
if __name__ == '__main__':
app.run(debug=True)
如果我们希望两个AntdButton
分别点击后,可以通过两个不同的回调函数对同一AntdPargraph
的内容进行输出,在之前的版本中默认会报下图所示的Duplicate callback outputs
错误:
在之前的版本中遇到这种情况解决方式也有很多,常用的如将多个回调函数整合为一个并在回调函数中,再基于dash.ctx.triggered_id
判断每次回调函数究竟是由哪个Input
触发的,这在较复杂回调功能的编写中就不太方便了。
而从Dash
2.9.0版本开始,为Output()
引入了bool型新参数allow_duplicate
,默认为False
,当设置为True
后,当前Output
便可以允许通过多个回调函数共同输出,将上面的例子回调部分进行改造,对后续重复的Output
设置allow_duplicate=True
:
@app.callback(
Output('output-demo', 'children', allow_duplicate=True),
Input('button-demo2', 'nClicks'),
prevent_initial_call=True
)
def trigger2(nClicks):
return f'按钮2: {nClicks}'
就可以不受限制啦~
当然,虽然有了这个新特性帮助我们解除了不少限制,但是我的建议还是不要滥用,它不一定可以使得我们的代码更简洁,基于dash.ctx.triggered_id
的分支判断在很多场景下还是更合适。
作为一个新的功能,allow_duplicate
目前在常规的服务端回调函数中运作正常,但在浏览器端回调函数中暂时无法使用,静待后续Dash
官方的更新。
Dash
2.9.0版本中新增参数局部快捷更新操作Patch()
,使得我们可以在回调函数中对目标属性进行局部更新,这样说起来还是比较抽象,我们举例说明:
假如我们的应用中要实现这样的交互逻辑:每点击一次AntdButton
,就会在下方AntdSpace
中新增一行文字内容,在**「以前」**的版本中,要实现这个功能,我们需要在回调函数中额外将目标AntdSpace
的children
属性作为State
传入,从而在每次回调执行时,将新的一行内容追加到先前状态的children
列表中,再进行输出:
import dash
import uuid
from dash import html
import feffery_antd_components as fac
from dash.dependencies import Input, Output, State
app = dash.Dash(__name__)
app.layout = html.Div(
[
fac.AntdButton(
'新增一行',
id='add-new-line'
),
fac.AntdSpace(
[],
id='target-container',
direction='vertical',
style={
'width': '100%'
}
)
],
style={
'padding': '50px 100px'
}
)
@app.callback(
Output('target-container', 'children'),
Input('add-new-line', 'nClicks'),
State('target-container', 'children'),
prevent_initial_call=True
)
def add_new_line(nClicks, origin_children):
return [
*origin_children,
str(uuid.uuid4())
]
if __name__ == '__main__':
app.run(debug=True)
这样做的弊端很明显——我们每次更新都需要先取回目标属性的现有状态,这带来了多余的资源消耗,而有了Patch()
模式,我们就可以将回调函数改写为下面的形式,实现相同的效果:
@app.callback(
Output('target-container', 'children'),
Input('add-new-line', 'nClicks'),
prevent_initial_call=True
)
def add_new_line(nClicks):
patch = dash.Patch()
patch.append(str(uuid.uuid4()))
return patch
相当于在回调函数中通过实例化Patch
,创建了针对目标Output
的远程代理对象,在回调函数中针对该代理对象的各种常用操作,都会在回调函数执行后落实到用户浏览器中的目标属性上.