本文示例代码已上传至我的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 字典化角色编排

我们可以用字典来分别编排各类型的角色,其中具体可细分为:

  • InputState字典化

当仅对回调函数的InputState角色进行字典化编排时,我们可以通过自定义的键值对,完成针对回调函数输入参数的映射,改造后的示例回调函数如下:

@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公众号。

在Dash中更灵活地编写回调函数的更多相关文章

  1. 在Java中如何编写回调函数,以及回调函数的简单应用

    import static java.lang.System.out; import static java.lang.System.err; import java.util.logging.Lev ...

  2. 如何更好的编写async函数

    2018年已经到了5月份,node的4.x版本也已经停止了维护 我司的某个服务也已经切到了8.x,目前正在做koa2.x的迁移 将之前的generator全部替换为async 但是,在替换的过程中,发 ...

  3. C#中委托、事件和回调函数的理解

    在C#中我们经常会碰到事件,尤其是在WPF或者WinForm中,窗体加载.或者点击一个按钮,都会触发事件.实际上,事件是对委托的封装.如果不进行封装,让委托暴露给调用者,调用者就可以把委托变量重新引用 ...

  4. extjs中组件监听器里面的回调函数说明

    近期在看项目源代码的时候发现了例如以下代码,当中_searchSupplierStore是JsonStore对象 _searchSupplierStore.on('beforeload',functi ...

  5. Opengl中的GLUT下的回调函数

    void glutDisplayFunc(void (*func)(void)); 注册当前窗口的显示回调函数 参数: func:形为void func()的函数,完成具体的绘制操作 这个函数告诉GL ...

  6. JavaScript ES7 中使用 async/await 解决回调函数嵌套问题

    原文链接:http://aisk.me/using-async-await-to-avoid-callback-hell/ JavaScript 中最蛋疼的事情莫过于回调函数嵌套问题.以往在浏览器中, ...

  7. jQuery中ajax方法无法执行回调函数问题

    最近遇到一个问题,发现使用jquery的ajax方法时,回调方法无法执行,而使用$.load()方法时却能正确返回数据.经过长时间调试最终发现是自己粗心大意,原来后台返回的是json数据,而返回的数据 ...

  8. 解决有关flask-socketio中服务端和客户端回调函数callback参数的问题(全网最全)

    由于工作当中需要用的flask_socketio,所以自己学习了一下如何使用,查阅了有关文档,当看到回调函数callback的时候,发现文档里都描述的不太清楚,最后终于琢磨出来了,分享给有需要的朋友 ...

  9. 详解C#中通过委托来实现回调函数功能的方法

    委托:类型安全的指向函数的指针 使用步骤 1:声明一个委托 delegate string DelString(string s) 2:定义一个委托变量 DelString del = new Del ...

  10. 使用代码给Unity中的动画片段绑定回调函数

    在制作动作游戏的时候,需要播放许多动画,同时还有个需求,那就是动画播放到一定时间时,给一个回调函数,好做对应的状态变更, 我查了一下,发现如果使用的是unity自带的动画系统,要做到这样的话,需要这样 ...

随机推荐

  1. FAQ:Linux 查看服务器型号(R730为例)

    命令:dmidecode -t system | grep -e Manufacturer -e Product 查询结果: Manufacturer: Dell Inc. Product Name: ...

  2. 并发编程-FutureTask解析

    1.FutureTask对象介绍 Future对象大家都不陌生,是JDK1.5提供的接口,是用来以阻塞的方式获取线程异步执行完的结果. 在Java中想要通过线程执行一个任务,离不开Runnable与C ...

  3. DBSCAN聚类

    一.概述   DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法,簇集的划定完全由样本的聚集 ...

  4. 应用层协议之DNS、DHCP

    运输层为应用进程提供了端对端的通信服务,但不同的网络应用的应用进程之间,还需要有不同的通信规则.因此在运输层协议之上,还需要有应用层协议. 应用层中有这些常见的协议 域名系统:DNS 动态主机配置:D ...

  5. 使用TypeScript类型注解,编写更干净的JS代码

    TypeScript 可以看作是 JavaScript 的超集,不仅包含了 JavaScript 的所有内容,还拓展了语法.规定了类型约束,使得我们可以编写更干净.完整的代码. 类型注解 TypeSc ...

  6. 【python】调用钉钉机器人发起通知

    有时候需要做个某些服务的状态监控,用钉钉机器人发通知挺方便的.可以用shell脚本配合crontab检测状态,检测到异常就调用python脚本发起告警. python内容 此处用的python3,需要 ...

  7. 组合查询(left_inner_right)与排序(order by _DESC _ASC)在题目中的应用

    1,想要让哪一列放在开头或者结尾,只需要将select中的查询位置放在最开始或者结尾即可: 2,组合查询要注意使用 on 加上组合条件: 3,order by 默认升序(ASC),降序使用:order ...

  8. Inno SetUp安装包:如何在程序安装时卸载驱动程序

    pnputil命令行方式卸载 如果您想通过命令行卸载.INF文件的驱动程序,您需要使用PnPUtil命令.以下是一个示例: pnputil /delete-driver oem0.inf /unins ...

  9. uni-app+h5puls 编写相机拍照

    <template> <view class="camera-page"> <image :src="imgSrc" v-if=& ...

  10. xlsx和path的运用

    从后端获取Excel模板 app.get('/api/download-template', (req, res) => { const templatePath = path.join(__d ...