添加微信号"CNFeffery"加入技术交流群
❝本文示例代码已上传至我的
❞Github
仓库https://github.com/CNFeffery/DataScienceStudyNotes
1 简介
这是我的系列教程「Python+Dash快速web应用开发」的第三期,在前两期的教程中,我们围绕什么是Dash
,以及如何配合方便好用的第三方拓展dash-bootstrap-components
来为我们的Dash
应用设计布局展开了非常详细的介绍。
而Dash
最吸引我的地方在于其高度封装了react.js
,使得我们无需编写js
语句,纯Python
编程就可以实现浏览器前端与后端计算之间常规的异步通信,从而创造出功能强大的交互式web
应用。
从今天的文章开始,我就将开始带大家走进Dash
的核心内容——「回调」。
2 Dash中的基础回调
2.1 最基础的回调
Dash
中的「回调」(callback)是以装饰器的形式,配合自编回调函数,实现前后端异步通信交互,这句话可能不太好理解,我们从一个简单的例子出发来认识Dash
中的「回调」:
❝app1.py
❞
-
import dash
-
import dash_html_components as html
-
import dash_bootstrap_components as dbc
-
from dash.dependencies
import Input, Output
-
-
app = dash.Dash(
-
__name__,
-
external_stylesheets=[
'css/bootstrap.min.css']
-
)
-
-
app.layout = html.Div(
-
[
-
html.Br(),
-
html.Br(),
-
html.Br(),
-
dbc.Container(
-
[
-
dbc.Row(
-
[
-
dbc.Col(dbc.Input(id=
'input-value',
-
placeholder=
'请输入些东西'),
-
width=
12),
-
dbc.Col(dbc.Label(id=
'output-value'),
-
width=
12)
-
]
-
)
-
]
-
)
-
]
-
)
-
-
-
# 对应app实例的回调函数装饰器
-
@app.callback(
-
Output(
'output-value',
'children'),
-
Input(
'input-value',
'value')
-
)
-
def input_to_output(input_value):
-
''
'
-
简单的回调函数
-
'
''
-
return input_value
-
-
-
if __name__ ==
'__main__':
-
app.run_server()
先来看看app1
的交互效果:
下面我们来分解上面的代码,梳理一下要构造一个具有实际交互功能的Dash
应用需要做什么:
「确定输入与输出部件」
一个可交互的系统一定是有「输入」与「输出」的,我们开头导入的Input
与Output
对象,他们分别扮演着「输入者」与「输出者」两种角色,其各自的第一个参数component_id
用于联动前端部分定义的部件。
我们在前面定义前端部件时,为dbc.Input
对应的输入框设置了id='input-value'
,为dbc.Label
对应的文字输出设置了id='output-value'
,让它们作为第一个参数可以被Input()
与Output()
唯一识别出来。
「确定输入与输出内容」
在确定了「输入者」与「输出者」之后,更重要的是为告诉Dash
需要监听什么输入,响应什么输出,这就要用到第二个参数component_property
。
它与对应的前端部件有关,譬如我们的dbc.Input()
输入框,其被输入的内容都存在value
属性中,而children
属性是dbc.Label
以及绝大多数html
部件的第一个参数,这样我们就确定了输入输出内容。
「装饰回调函数」
app.callback()
装饰器按照规定的先Output()
后Input()
的顺序传入相应对象,而既然是装饰器,自然需要配合自定义回调函数使用。
我们的input_to_output()
就是对应的回调函数,其参数与装饰器中的Input()
对应,而函数内部则用来定义计算处理过程。
最后return
的对象则对应Output()
。
-
# 对应app实例的回调函数装饰器
-
@app.callback(
-
Output(
'output-value',
'children'),
-
Input(
'input-value',
'value')
-
)
-
def input_to_output(input_value):
-
''
'
-
简单的回调函数
-
'
''
-
return input_value
通过上面这样的结构,我们得以纯Python
“寥寥数语”实现了交互功能,赋予我们编写任意功能Dash
应用的能力。
2.2 同时设置多个Input()与Output()
在上一小节中我们介绍的是最基本的「单输入 -> 单输出」回调模式,很多时候我们需要更复杂的回调模式,譬如下面的例子:
❝app2.py
❞
-
import dash
-
import dash_html_components as html
-
import dash_bootstrap_components as dbc
-
from dash.dependencies
import Input, Output
-
-
app = dash.Dash(
-
__name__,
-
external_stylesheets=[
'css/bootstrap.min.css']
-
)
-
-
app.layout = html.Div(
-
[
-
html.Br(),
-
html.Br(),
-
html.Br(),
-
dbc.Container(
-
[
-
dbc.Row(
-
[
-
dbc.Col(dbc.Input(id=
'input-value1'), width=
3),
-
dbc.Col(html.P(
'+'), width=
1),
-
dbc.Col(dbc.Input(id=
'input-value2'), width=
3),
-
],
-
justify=
'start'
-
),
-
html.Hr(),
-
dbc.Label(id=
'output-value')
-
]
-
)
-
]
-
)
-
-
-
@app.callback(
-
Output(
'output-value',
'children'),
-
Input(
'input-value1',
'value'),
-
Input(
'input-value2',
'value')
-
)
-
def input_to_output(input_value1, input_value2):
-
-
try:
-
return float(input_value1) + float(input_value2)
-
except:
-
return
'请输入合法参数!'
-
-
-
if __name__ ==
'__main__':
-
app.run_server()
图3
这里我们的Input()
对象不止一个,在Output()
对象之后依次传入(也可以把所有Input()
对象包在一个列表中传入),其顺序对应后面回调函数的参数顺序,从而实现了多个输入值的一一对应。
同样的,Output()
也可以有多个:
❝app3.py
❞
-
import dash
-
import dash_html_components as html
-
import dash_bootstrap_components as dbc
-
from dash.dependencies
import Input, Output
-
-
app = dash.Dash(
-
__name__,
-
external_stylesheets=[
'css/bootstrap.min.css']
-
)
-
-
app.layout = html.Div(
-
[
-
html.Br(),
-
html.Br(),
-
html.Br(),
-
dbc.Container(
-
[
-
dbc.Row(
-
[
-
dbc.Col(dbc.Input(id=
'input-lastname'), width=
3),
-
dbc.Col(html.P(
'+'), width=
1),
-
dbc.Col(dbc.Input(id=
'input-firstname'), width=
3),
-
],
-
justify=
'start'
-
),
-
html.Hr(),
-
dbc.Label(id=
'output1'),
-
html.Br(),
-
dbc.Label(id=
'output2')
-
]
-
)
-
]
-
)
-
-
-
@app.callback(
-
[Output(
'output1',
'children'),
-
Output(
'output2',
'children')],
-
[Input(
'input-lastname',
'value'),
-
Input(
'input-firstname',
'value')]
-
)
-
def input_to_output(lastname, firstname):
-
-
try:
-
return
'完整姓名:' + lastname + firstname, f
'姓名长度为{len(lastname+firstname)}'
-
except:
-
return
'等待输入...',
'等待输入...'
-
-
-
if __name__ ==
'__main__':
-
app.run_server()
图4
可以看到不管是多个Output()
还是Input()
,只需要嵌套在列表中即可。
2.3 利用State()实现惰性交互
很多情况下,如果我们的回调函数计算过程时间开销较大,那么像前面介绍的仅靠Input()
与Output()
实现的前后端通信会很频繁,因为监听到的所有输入部件对应属性值只要略一改变,就会触发回调。
为了解决这类问题,Dash
中设计了State()
对象,我们可以利用State()
替换Input()
来绑定对应的输入值,再将一些需要主动触发的譬如dbc.Button()
按钮部件的属性n_clicks
,作为Input()
对象进行绑定。
让我们通过下面的例子更好的理解它的作用:
❝app4.py
❞
-
import dash
-
import dash_html_components as html
-
import dash_bootstrap_components as dbc
-
from dash.dependencies
import Input, Output, State
-
-
app = dash.Dash(
-
__name__,
-
external_stylesheets=[
'css/bootstrap.min.css']
-
)
-
-
app.layout = html.Div(
-
[
-
html.Br(),
-
html.Br(),
-
html.Br(),
-
dbc.Container(
-
[
-
dbc.Row(
-
[
-
dbc.Col(dbc.Input(id=
'input-value'),
-
width=
4),
-
dbc.Col(dbc.Button(
'小写转大写',
-
id=
'state-button',
-
n_clicks=
0),
-
width=
4),
-
dbc.Col(dbc.Label(id=
'output-value',
-
style={
'padding':
'0',
-
'margin':
'0',
-
'line-height':
'38px'}),
-
width=
4)
-
],
-
justify=
'start'
-
)
-
]
-
)
-
]
-
)
-
-
-
@app.callback(
-
Output(
'output-value',
'children'),
-
Input(
'state-button',
'n_clicks'),
-
State(
'input-value',
'value')
-
-
)
-
def input_to_output(n_clicks, value):
-
-
if n_clicks:
-
return value.upper()
-
-
-
if __name__ ==
'__main__':
-
app.run_server()
图5
可以看到,装饰器中按照Output()
、Input()
、State()
的顺序传入各个对象后,我们的Button()
部件的n_clicks
参数记录了对应的按钮被点击了多少次,初始化我们设置其为0,之后每次等我们输入完单词,主动去点击按钮从而增加其被点击次数记录时,回调函数才会被触发,这样就方便了我们的很多复杂应用场景~
以上就是本期的全部内容,欢迎在评论区与我进行讨论~
加入知识星球【我们谈论数据科学】
300+小伙伴一起学习!
· 推荐阅读 ·
转载:https://blog.csdn.net/baoqiangwang/article/details/113011859