如何在Python Shiny中仅执行一次函数并展示其两个输出?
问题:Python Shiny中仅执行一次高计算量函数,多卡片复用输出
我有一个遍历数据并生成两个输出的函数(示例中的check()函数),希望将这两个输出分别展示在不同的卡片中。当前实现中check()函数会被执行两次,处理真实数据时因高计算量导致效率极低。请问是否有办法在Python Shiny中仅运行该函数一次,却仍能在不同卡片中使用其两个输出?
最小可复现示例(MRE)
from shiny import App, render, ui, reactive from pathlib import Path app_ui = ui.page_fillable( ui.layout_sidebar( ui.sidebar( ui.input_text("numbers", "Number you want to check", value="1,2,3,4,5,6,7,8"), ui.input_action_button("check_numbers", "Check your numbers") ), ui.output_ui("numbers_frames") ) ) def server(input, output, session): @render.ui @reactive.event(input.check_numbers) def numbers_frames(): return ui.TagList( ui.layout_columns( ui.card( ui.card_header("even"), ui.output_text("even"), ), ui.card( ui.card_header("odd"), ui.output_text("odd"), ), ) ) @reactive.event(input.check_numbers) def check(): even = [] odd = [] for number in input.numbers().split(','): number_int = int(number.strip()) if number_int % 2 == 0: even.append(str(number_int)) else: odd.append(str(number_int)) print("check() has been exectuted") return (even, odd) @output @render.text def even(): even_output = check()[0] return ','.join(even_output) @output @render.text def odd(): odd_output = check()[1] return ','.join(odd_output) src_dir = Path(__file__).parent / "src" app = App(app_ui, server, static_assets=src_dir)
解决方案
方法1:使用@reactive.cached缓存函数结果
给check()函数添加@reactive.cached装饰器,函数结果会被缓存,多次调用时不会重复执行计算,仅在触发事件(点击按钮)时重新计算一次。
修改后的核心代码:
@reactive.cached @reactive.event(input.check_numbers) def check(): even = [] odd = [] for number in input.numbers().split(','): number_int = int(number.strip()) if number_int % 2 == 0: even.append(str(number_int)) else: odd.append(str(number_int)) print("check() has been executed") return (even, odd) @output @render.text def even(): return ','.join(check()[0]) @output @render.text def odd(): return ','.join(check()[1])
方法2:使用reactive.Value存储计算结果
通过reactive.Value保存check()的执行结果,按钮点击时触发计算并更新这个值,两个输出组件直接读取缓存的值,避免重复调用函数。
修改后的核心代码:
def server(input, output, session): # 初始化reactive.Value存储结果 check_result = reactive.Value(tuple()) @render.ui @reactive.event(input.check_numbers) def numbers_frames(): return ui.TagList( ui.layout_columns( ui.card( ui.card_header("even"), ui.output_text("even"), ), ui.card( ui.card_header("odd"), ui.output_text("odd"), ), ) ) # 按钮点击时执行计算并更新缓存 @reactive.Effect @reactive.event(input.check_numbers) def _run_check(): even = [] odd = [] for number in input.numbers().split(','): number_int = int(number.strip()) if number_int % 2 == 0: even.append(str(number_int)) else: odd.append(str(number_int)) print("check() has been executed") check_result.set((even, odd)) @output @render.text def even(): return ','.join(check_result()[0]) @output @render.text def odd(): return ','.join(check_result()[1])
两种方法都能实现仅执行一次计算逻辑,然后在不同卡片中复用输出结果,解决高计算量场景下的效率问题。
内容的提问来源于stack exchange,提问作者gernophil
相关产品推荐
相关产品推荐

