<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python+Dash快速web應(yīng)用開發(fā):回調(diào)交互篇(中)

          共 11732字,需瀏覽 24分鐘

           ·

          2021-02-01 20:17


          添加微信號"CNFeffery"加入技術(shù)交流群

          ?

          本文示例代碼已上傳至我的Github倉庫https://github.com/CNFeffery/DataScienceStudyNotes

          ?

          1 簡介

          這是我的系列教程「Python+Dash快速web應(yīng)用開發(fā)」的第四期,在上一期的文章中,我們進(jìn)入了Dash核心內(nèi)容——callback,get到如何在不編寫js代碼的情況下,輕松實(shí)現(xiàn)前后端異步通信,為創(chuàng)造任意交互方式的Dash應(yīng)用打下基礎(chǔ)。

          而在今天的文章中,我將帶大家學(xué)習(xí)有關(guān)Dash「回調(diào)」的一些非常實(shí)用,且不算復(fù)雜的額外特性,讓你更加熟悉Dash的回調(diào)交互~

          圖1

          2 Dash中的回調(diào)實(shí)用小特性

          2.1 靈活使用debug模式

          開發(fā)階段,在Dash中使用run_server()啟動我們的應(yīng)用時,可以添加參數(shù)debug=True來切換為「debug」模式,在這種模式下,我們可以獲得以下輔助功能:

          • 「熱重載」

          熱重載指的是,我們在編寫完一個Dash的完整應(yīng)用并在debug模式下啟動之后,在保持應(yīng)用運(yùn)行的情況下,修改源代碼并保存之后,瀏覽器中運(yùn)行的Dash實(shí)例會自動重啟刷新,就像下面的例子一樣:

          ?

          app1.py

          ?
          import?dash
          import?dash_html_components?as?html

          app?=?dash.Dash(__name__)

          app.layout?=?html.Div(
          ????html.H1('我是熱重載之前!')
          )

          if?__name__?==?'__main__':
          ????app.run_server(debug=True)
          圖2

          可以看到,debug模式下,我們對源代碼做出的修改在保存之后,都會受到Dash的監(jiān)聽,從而做出反饋(注意一定要在作出修改的代碼完整之后再保存,否則代碼寫到一半就保存會引起語法錯誤等中斷當(dāng)前Dash實(shí)例)。

          • 「對回調(diào)結(jié)構(gòu)進(jìn)行可視化」

          你可能已經(jīng)注意到,在開啟debug模式之后,我們?yōu)g覽器中的Dash應(yīng)用右下角出現(xiàn)的藍(lán)色logo,點(diǎn)擊打開折疊,可以看到幾個按鈕:

          圖3

          其中第一個「Callbacks」非常有意思,它可以幫助我們對當(dāng)前Dash應(yīng)用中的回調(diào)關(guān)系進(jìn)行可視化,譬如下面的例子:

          ?

          app2.py

          ?
          import?dash
          import?dash_bootstrap_components?as?dbc
          import?dash_html_components?as?html
          from?dash.dependencies?import?Input,?Output

          app?=?dash.Dash(
          ????__name__,
          ????external_stylesheets=['css/bootstrap.min.css']
          )

          app.layout?=?html.Div(
          ????dbc.Container(
          ????????[
          ????????????html.Br(),
          ????????????html.Br(),
          ????????????html.Br(),
          ????????????dbc.Row(
          ????????????????[
          ????????????????????dbc.Col(
          ????????????????????????dbc.Input(id='input1'),
          ????????????????????????width=4
          ????????????????????),
          ????????????????????dbc.Col(
          ????????????????????????dbc.Label(id='output1'),
          ????????????????????????width=4
          ????????????????????)
          ????????????????]
          ????????????),
          ????????????dbc.Row(
          ????????????????[
          ????????????????????dbc.Col(
          ????????????????????????dbc.Input(id='input2'),
          ????????????????????????width=4
          ????????????????????),
          ????????????????????dbc.Col(
          ????????????????????????dbc.Label(id='output2'),
          ????????????????????????width=4
          ????????????????????)
          ????????????????]
          ????????????)
          ????????]
          ????)
          )

          @app.callback(
          ????Output('output1',?'children'),
          ????Input('input1',?'value')
          )
          def?callback1(value):

          ????if?value:
          ????????return?int(value)?**?2


          @app.callback(
          ????Output('output2',?'children'),
          ????Input('input2',?'value')
          )
          def?callback2(value):

          ????if?value:
          ????????return?int(value)?**?0.5

          if?__name__?==?"__main__":
          ????app.run_server(debug=True)
          圖4

          可以看到,我們打開「Callbacks」之后,可以看到每個回調(diào)的輸入輸出、通信延遲等信息,可以幫助我們更有條理的組織各個回調(diào)。

          • 「展示運(yùn)行錯誤信息」

          既然主要功能是debug,自然是可以幫助我們在程序出現(xiàn)錯誤時打印具體的錯誤信息,我們在前面app2.py例子的基礎(chǔ)上,故意制造一些錯誤(此處代碼粘貼有誤,請查看評論區(qū)說明):

          ?

          app3.py

          ?
          import?dash
          import?dash_bootstrap_components?as?dbc
          import?dash_core_components?as?dcc
          import?dash_html_components?as?html

          app?=?dash.Dash(
          ????__name__,
          ????external_stylesheets=['css/bootstrap.min.css']
          )

          app.layout?=?html.Div(
          ????[
          ????????#?fluid默認(rèn)為False
          ????????dbc.Container(
          ????????????[
          ????????????????dcc.Dropdown(),
          ????????????????'測試',
          ????????????????dcc.Dropdown()
          ????????????]
          ????????),

          ????????html.Hr(),?#?水平分割線

          ????????#?fluid設(shè)置為True
          ????????dbc.Container(
          ????????????[
          ????????????????dcc.Dropdown(),
          ????????????????'測試',
          ????????????????dcc.Dropdown()
          ????????????],
          ????????????fluid=True
          ????????)
          ????]
          )

          if?__name__?==?"__main__":
          ????app.run_server()
          圖5

          可以看到,我們故意制造出的兩種錯誤:「不處理Input()默認(rèn)的缺失值value」、「Output()傳入不存在的id」,都在瀏覽器中得到輸出,并且可自由查看錯誤信息,這對我們開發(fā)過程幫助很大。

          2.2 阻止應(yīng)用的初始回調(diào)

          在前面的app3例子中,我們故意制造出的錯誤之一是「不處理Input()默認(rèn)的缺失值value」,這里的錯誤展開來說是因?yàn)?code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">Input()部件value屬性的默認(rèn)值是None,使得剛載入應(yīng)用還未輸入值時引發(fā)了回調(diào)中計(jì)算部分的邏輯錯誤。

          類似這樣的情況很多,可以通過給部件相應(yīng)屬性設(shè)置默認(rèn)值或者在回調(diào)中寫條件判斷等方式處理,就像app2中那樣,但如果這樣的部件比較多,一個一個逐一處理還是比較繁瑣,而Dash中提供了「阻止初始回調(diào)」的特性,只需要在app.callback裝飾器中設(shè)置參數(shù)prevent_initial_call=True即可:

          ?

          app4.py

          ?
          import?dash
          import?dash_bootstrap_components?as?dbc
          import?dash_html_components?as?html
          from?dash.dependencies?import?Input,?Output

          app?=?dash.Dash(
          ????__name__,
          ????external_stylesheets=['css/bootstrap.min.css']
          )

          app.layout?=?html.Div(
          ????dbc.Container(
          ????????[
          ????????????html.Br(),
          ????????????html.Br(),
          ????????????html.Br(),
          ????????????dbc.Row(
          ????????????????[
          ????????????????????dbc.Col(
          ????????????????????????dbc.Input(id='input1'),
          ????????????????????????width=4
          ????????????????????),
          ????????????????????dbc.Col(
          ????????????????????????dbc.Label(id='output1'),
          ????????????????????????width=4
          ????????????????????)
          ????????????????]
          ????????????)
          ????????]
          ????)
          )


          @app.callback(
          ????Output('output1',?'children'),
          ????Input('input1',?'value'),
          ????prevent_initial_call=True
          )
          def?callback1(value):

          ????return?int(value)?**?2

          if?__name__?==?"__main__":
          ????app.run_server(debug=True)
          圖6

          可以看到,設(shè)置完參數(shù)后,Dash應(yīng)用被訪問時,不會自動執(zhí)行首次回調(diào),非常的方便。

          2.3 忽略回調(diào)匹配錯誤

          在前面我們還制造出了「Output()傳入不存在的id」這種錯誤,也就是回調(diào)函數(shù)查找輸入輸出等關(guān)系時,出現(xiàn)匹配失敗的情況。

          但在很多時候,我們需要在發(fā)生某些交互回調(diào)時,才創(chuàng)建返回一些具有指定「id」的部件,這時如果程序中提前寫好了針對這些初始化時「不存在」的部件的回調(diào),就會觸發(fā)前面的錯誤。

          Dash中提供了解決此類問題的方法,在創(chuàng)建app實(shí)例時添加參數(shù)suppress_callback_exceptions=True即可:

          ?

          app5.py

          ?
          import?dash
          import?dash_bootstrap_components?as?dbc
          import?dash_html_components?as?html
          import?dash_core_components?as?dcc
          from?dash.dependencies?import?Input,?Output

          app?=?dash.Dash(
          ????__name__,
          ????external_stylesheets=['css/bootstrap.min.css'],
          ????#?suppress_callback_exceptions=True
          )

          app.layout?=?html.Div(
          ????dbc.Container(
          ????????[
          ????????????dbc.Row(
          ????????????????[
          ????????????????????dbc.Col(
          ????????????????????????dbc.Input(id='input_num')
          ????????????????????),
          ????????????????????dbc.Col(id='output_item')
          ????????????????]
          ????????????),
          ????????????dbc.Row(
          ????????????????dbc.Col(
          ????????????????????dbc.Label(id='output_desc')
          ????????????????)
          ????????????)
          ????????]
          ????)
          )


          @app.callback(
          ????Output('output_item',?'children'),
          ????Input('input_num',?'value'),
          ????prevent_initial_call=True
          )
          def?callback1(value):
          ????return?dcc.Dropdown(
          ????????id='output_dropdown',
          ????????options=[
          ????????????{'label':?i,?'value':?i}
          ????????????for?i?in?range(int(value))
          ????????]
          ????)


          @app.callback(
          ????Output('output_desc',?'children'),
          ????Input('output_dropdown',?'options'),
          ????prevent_initial_call=True
          )
          def?callback2(options):
          ????return?'生成的Dropdown部件共有{}個選項(xiàng)'.format(options.__len__())

          if?__name__?==?"__main__":
          ????app.run_server(debug=True)
          圖7

          可以看到,參數(shù)添加后,Dash會自動忽略類似的回調(diào)匹配錯誤,非常的實(shí)用,這個知識點(diǎn)我們會在以后的「前后端分離」篇中頻繁地使用到,所以一定要記住它。

          3 編寫一個貸款計(jì)算器

          get完今天所學(xué)的知識點(diǎn)后,我們通過實(shí)際的例子,來鞏固上一期及這一期的內(nèi)容,幫助大家對Dash中的回調(diào)基礎(chǔ)知識有更好的理解。

          今天我們要編寫的例子,是貸款計(jì)算器,要編寫出一個實(shí)際的貸款計(jì)算器,我們需要組織以下用戶輸入內(nèi)容:

          • 「貸款總金額」
          • 「還款月份數(shù)量」
          • 「年利率」
          • 「還款方式」

          其中還款方式主要有「等額本息」「等額本金」兩種,我們利用之前介紹過的dash-bootstrap-components來搭建頁面,其中「貸款金額」「還款月份數(shù)量」以及「年利率」我們都使用Input()部件來實(shí)現(xiàn),并利用參數(shù)type="number"來約束其類型為數(shù)值。

          「還款方式」是二選一,所以我們使用部件RadioItems()來實(shí)現(xiàn),最后設(shè)置計(jì)算按鈕,配合以前介紹過的State()n_clicks來交互執(zhí)行計(jì)算,并以plotly.express折線圖的形式呈現(xiàn)計(jì)算結(jié)果(這部分我們將在之后的「嵌入可視化」中詳細(xì)介紹),最終得到的效果如下:

          圖8

          代碼如下:

          ?

          app6.py

          ?
          import?dash
          import?dash_html_components?as?html
          import?plotly.express?as?px
          import?dash_core_components?as?dcc
          import?dash_bootstrap_components?as?dbc
          from?dash.dependencies?import?Output,?Input,?State
          import?time

          app?=?dash.Dash(
          ????__name__,
          ????external_stylesheets=['css/bootstrap.min.css'],
          ????suppress_callback_exceptions=True
          )

          app.layout?=?html.Div(
          ????dbc.Container(
          ????????[
          ????????????html.Br(),
          ????????????html.Br(),
          ????????????html.Br(),
          ????????????html.Br(),
          ????????????dbc.Row(
          ????????????????dbc.Col(
          ????????????????????dbc.InputGroup(
          ????????????????????????[
          ????????????????????????????dbc.InputGroupAddon("貸款金額",?addon_type="prepend"),
          ????????????????????????????dbc.Input(
          ????????????????????????????????id='loan_amount',
          ????????????????????????????????placeholder='請輸入貸款總金額',
          ????????????????????????????????type="number",
          ????????????????????????????????value=100
          ????????????????????????????),
          ????????????????????????????dbc.InputGroupAddon("萬元",?addon_type="append"),
          ????????????????????????],
          ????????????????????),
          ????????????????????width={'size':?6,?'offset':?3}
          ????????????????)
          ????????????),
          ????????????html.Br(),
          ????????????dbc.Row(
          ????????????????dbc.Col(
          ????????????????????dbc.InputGroup(
          ????????????????????????[
          ????????????????????????????dbc.InputGroupAddon("計(jì)劃還款月數(shù)",?addon_type="prepend"),
          ????????????????????????????dbc.Input(
          ????????????????????????????????id='repay_month_amount',
          ????????????????????????????????placeholder='請輸入計(jì)劃還款月數(shù)',
          ????????????????????????????????type="number",
          ????????????????????????????????value=24,
          ????????????????????????????????min=1,
          ????????????????????????????????step=1
          ????????????????????????????),
          ????????????????????????????dbc.InputGroupAddon("個月",?addon_type="append"),
          ????????????????????????],
          ????????????????????),
          ????????????????????width={'size':?6,?'offset':?3}
          ????????????????)
          ????????????),
          ????????????html.Br(),
          ????????????dbc.Row(
          ????????????????dbc.Col(
          ????????????????????dbc.InputGroup(
          ????????????????????????[
          ????????????????????????????dbc.InputGroupAddon("年利率",?addon_type="prepend"),
          ????????????????????????????dbc.Input(
          ????????????????????????????????id='interest_rate',
          ????????????????????????????????placeholder='請輸入年利率',
          ????????????????????????????????type="number",
          ????????????????????????????????value=5,
          ????????????????????????????????min=0,
          ????????????????????????????????step=0.001
          ????????????????????????????),
          ????????????????????????????dbc.InputGroupAddon("%",?addon_type="append"),
          ????????????????????????],
          ????????????????????),
          ????????????????????width={'size':?6,?'offset':?3}
          ????????????????)
          ????????????),
          ????????????html.Br(),
          ????????????dbc.Row(
          ????????????????dbc.Col(
          ????????????????????dbc.RadioItems(
          ????????????????????????id="repay_method",
          ????????????????????????options=[
          ????????????????????????????{"label":?"等額本息",?"value":?"等額本息"},
          ????????????????????????????{"label":?"等額本金",?"value":?"等額本金"}
          ????????????????????????],
          ????????????????????????value='等額本息'
          ????????????????????),
          ????????????????????width={'size':?6,?'offset':?3}
          ????????????????),
          ????????????),
          ????????????html.Br(),
          ????????????dbc.Row(
          ????????????????dbc.Col(
          ????????????????????dbc.Button('開始計(jì)算',?id='start',?n_clicks=0,?color='light'),
          ????????????????????width={'size':?6,?'offset':?3}
          ????????????????),
          ????????????),
          ????????????html.Br(),
          ????????????dbc.Row(
          ????????????????dbc.Col(
          ????????????????????dcc.Loading(dcc.Graph(id='repay_timeline')),
          ????????????????????width={'size':?6,?'offset':?3}
          ????????????????),
          ????????????),
          ????????],
          ????????fluid=True
          ????)
          )


          def?make_line_graph(loan_amount,
          ????????????????????repay_month_amount,
          ????????????????????interest_rate,
          ????????????????????repay_method)
          :

          ????interest_rate?/=?100
          ????loan_amount?*=?10000

          ????month_interest_rate?=?interest_rate?/?12

          ????if?repay_method?==?'等額本息':

          ????????month_repay?=?loan_amount?*?month_interest_rate?*?pow((1?+?month_interest_rate),?repay_month_amount)?/?\
          ??????????????????????(pow((1?+?month_interest_rate),?repay_month_amount)?-?1)

          ????????month_repay?=?round(month_repay,?2)

          ????????month_repay?=?[month_repay]?*?repay_month_amount

          ????else:

          ????????d?=?loan_amount?/?repay_month_amount
          ????????month_repay?=?[round(d?+?(loan_amount?-?d?*?(month?-?1))?*?month_interest_rate,?3)
          ???????????????????????for?month?in?range(1,?repay_month_amount?+?1)]

          ????fig?=?px.line(x=[f'第{i}月'?for?i?in?range(1,?repay_month_amount?+?1)],
          ??????????????????y=month_repay,
          ??????????????????title='每月還款金額變化曲線(總支出:{}元)'.format(round(sum(month_repay),?2)),
          ??????????????????template='plotly_white')

          ????return?fig

          @app.callback(
          ????Output('repay_timeline',?'figure'),
          ????Input('start',?'n_clicks'),
          ????[State('loan_amount',?'value'),
          ?????State('repay_month_amount',?'value'),
          ?????State('interest_rate',?'value'),
          ?????State('repay_method',?'value')],
          ????prevent_initial_call=True
          )
          def?refresh_repay_timeline(n_clicks,?loan_amount,?repay_month_amount,?interest_rate,?repay_method):
          ????time.sleep(0.2)?#?增加應(yīng)用的動態(tài)效果

          ????return?make_line_graph(loan_amount,?repay_month_amount,?interest_rate,?repay_method)


          if?__name__?==?'__main__':
          ????app.run_server(debug=True)

          以上就是本文全部內(nèi)容,下一期中將為大家介紹Dash中更加巧妙的回調(diào)技巧,敬請期待。歡迎在評論區(qū)中與我進(jìn)行討論~

          加入知識星球【我們談?wù)摂?shù)據(jù)科學(xué)】

          300+小伙伴一起學(xué)習(xí)!






          · 推薦閱讀?·

          在模仿中精進(jìn)數(shù)據(jù)可視化07:星球研究所大壩分布可視化

          Python+Dash快速web應(yīng)用開發(fā):回調(diào)交互篇(上)

          這個Pandas函數(shù)可以自動爬取Web圖表


          瀏覽 84
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91无码成人 | 欧美日韩人妻精品 | 久久只有精品 | 成人电影久久久 | 天堂中文字幕在线 |