本文示例代碼已上傳至我的
Github
倉庫https://github.com/CNFeffery/dash-master
大家好我是費老師,就在昨晚,Dash
框架發佈了其2.9.0版本更新,在一衆更新內容中,有兩條新特性在我看來尤爲重要,可以大幅度提升我們開發Dash
應用的效率,下面我就將帶大家一起了解它們的具體內容:
1 允許多個回調函數重複Output
在之前版本的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
官方的更新。
2 新增Patch()操作模式
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
的遠程代理對象,在回調函數中針對該代理對象的各種常用操作,都會在回調函數執行後落實到用戶瀏覽器中的目標屬性上,這聽起來可能有些抽象,我用下面的例子展示了基於Patch
可以實現的常用局部值操作(對應代碼受篇幅限制,請在文章開頭的github
倉庫中查看):
以上就是本文的全部內容,對Dash
應用開發感興趣的朋友,歡迎添加微信號CNFeffery
,備註“dash學習”加入我的技術交流羣,一起成長一起進步。