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