在Dash中更灵活地编写回调函数
本文示例代码已上传至我的
Github
仓库https://github.com/CNFeffery/dash-master
大家好我是费老师,使用Dash
开发过交互式应用的朋友,想必都不会对回调函数感到陌生,作为Dash
应用中实现各种交互逻辑的“万金油”方式,不管是常规的@app.callback()
,还是对应浏览器端回调的app.clientside_callback()
和ClientsideFunction()
,其中编排各种回调角色时,我们都是按照先Output
,再Input
,最后State
的顺序依次罗列的,且各个角色存在多个时,建议用[]
将它们包裹住,以提升代码可读性。
但这并不是不可打破的铁律,事实上,Dash
还额外提供了多种多样的回调角色编排方式,官方称之为Flexible Callback Signatures,从而解决单个回调函数中角色太多时代码可读性变差等问题,今天的文章中,我就将带大家学习相关的实用知识,从而更清晰地进行Dash
应用开发及维护😇。
阅读本文大约需要6分钟
为了方便演示,我们构造下图所示的简单示例Dash
应用(完整源码见文章开头地址):
如果要编排以两个按钮作为示例Input
角色,两个输入框作为示例State
角色,并向两个文字组件中分别Output
不同的参数值内容的回调函数,按照常规的写法,对应的回调函数可以写作下方形式:
@app.callback(
[Output('demo-output1', 'children'),
Output('demo-output2', 'children')],
[Input('demo-button1', 'nClicks'),
Input('demo-button2', 'nClicks')],
[State('demo-input1', 'value'),
State('demo-input2', 'value')],
prevent_initial_call=True
)
def demo_callback(nClicks1, nClicks2, value1, value2):
return [
f'nClicks1: {nClicks1}, nClicks2: {nClicks2}',
f'value1: {value1}, value2: {value2}'
]
下面我们以此为基础,分别介绍其他不同的写法:
1 字典化角色编排
我们可以用字典来分别编排各类型的角色,其中具体可细分为:
- 仅
Input
、State
字典化
当仅对回调函数的Input
和State
角色进行字典化编排时,我们可以通过自定义的键值对,完成针对回调函数输入参数的映射,改造后的示例回调函数如下:
@app.callback(
[Output('demo-output1', 'children'),
Output('demo-output2', 'children')],
inputs=dict(
nClicks1=Input('demo-button1', 'nClicks'),
nClicks2=Input('demo-button2', 'nClicks')
),
state=dict(
value1=State('demo-input1', 'value'),
value2=State('demo-input2', 'value')
),
prevent_initial_call=True
)
def demo_callback(nClicks1, nClicks2, value1, value2):
'''字典化角色编排:仅Input、State字典化'''
return [
f'nClicks1: {nClicks1}, nClicks2: {nClicks2}',
f'value1: {value1}, value2: {value2}'
]
- 全部角色字典化
如果我们将回调函数的Output
也进行了字典化改造,那么在回调函数中就需要返回对应键值对的字典(返回单个dash.no_update
时不受限制),示例写法如下:
@app.callback(
output=dict(
content1=Output('demo-output1', 'children'),
content2=Output('demo-output2', 'children')
),
inputs=dict(
nClicks1=Input('demo-button1', 'nClicks'),
nClicks2=Input('demo-button2', 'nClicks')
),
state=dict(
value1=State('demo-input1', 'value'),
value2=State('demo-input2', 'value')
),
prevent_initial_call=True
)
def demo_callback(nClicks1, nClicks2, value1, value2):
'''字典化角色编排:全部角色字典化'''
return dict(
content1=f'nClicks1: {nClicks1}, nClicks2: {nClicks2}',
content2=f'value1: {value1}, value2: {value2}'
)
通过字典化角色的形式,我们可以为每个角色自由起名字,建议是起跟功能相关的名字,如login_button_click
,或登录按钮点击
这样的中文键名,只要能帮助你更好地读懂回调函数逻辑就可以😉。
2 嵌套式字典化角色编排
当我们在使用上文所介绍的字典化角色编排方式时,除了在字典中平铺书写相应角色外,还可以向下继续进行字典嵌套,从而实现更自由的参数分组效果,相应的,对应输入参数也会以字典的形式传入内部的各键值对参数:
@app.callback(
output=dict(
content1=Output('demo-output1', 'children'),
content2=Output('demo-output2', 'children')
),
inputs=dict(
nClicks1=Input('demo-button1', 'nClicks'),
nClicks2=Input('demo-button2', 'nClicks')
),
state=dict(
input_values=dict(
value1=State('demo-input1', 'value'),
value2=State('demo-input2', 'value')
)
),
prevent_initial_call=True
)
def demo_callback(nClicks1, nClicks2, input_values):
'''嵌套式字典化角色编排'''
return dict(
content1=f'nClicks1: {nClicks1}, nClicks2: {nClicks2}',
content2='value1: {value1}, value2: {value2}'.format(**input_values)
)
3 对需要返回若干dash.no_update
的情况进行简化
针对字典化角色编排Output
的方式,当我们仅需要对部分输出目标返回实际值,对其余目标返回dash.no_update
时,可以配合标准库collections
中的defaultdict
以及dash
回调的上下文简化相关过程:
@app.callback(
output=dict(
content1=Output('demo-output1', 'children'),
content2=Output('demo-output2', 'children')
),
inputs=dict(
nClicks1=Input('demo-button1', 'nClicks'),
nClicks2=Input('demo-button2', 'nClicks')
),
state=dict(
value1=State('demo-input1', 'value'),
value2=State('demo-input2', 'value')
),
prevent_initial_call=True
)
def demo_callback(nClicks1, nClicks2, value1, value2):
'''字典化Output配合defaultdict'''
# 假设我们需要除了content1之外的其他角色默认输出为dash.no_update
output = defaultdict(
lambda: dash.no_update,
dict(
content1=f'nClicks1: {nClicks1}, nClicks2: {nClicks2}'
)
)
return {
key: output[key]
# 通过上下文遍历所有Output字典键名
for key in dash.ctx.outputs_grouping.keys()
}
其中构造defaultdict
并设置默认值等过程,我也会在fac
即将发布的0.3.x
版本中封装为一步到位的工具函数,毕竟这种场景在进阶Dash
应用的开发中还是很常用的,省得在常规方式中逐个写dash.no_update
或其他默认值。
除此之外,有关Flexible Callback Signatures还有一些其他的写法,但是在我看来并没有字典化写法这么实用,感兴趣的朋友可以移步https://dash.plotly.com/flexible-callback-signatures
了解更多。
以上就是本文的全部内容,更多有关dash
应用开发的前沿知识和技巧欢迎持续关注玩转dash公众号。