Dash 2.9.0版本重磅新功能一覽

本文示例代碼已上傳至我的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中新增一行文字內容,在以前的版本中,要實現這個功能,我們需要在回調函數中額外將目標AntdSpacechildren屬性作爲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學習”加入我的技術交流羣,一起成長一起進步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章