Flexible/programmatic variables to a callback

I have a variable number of inputs that I’ve named via list comprehension, e.g.:

[[dcc.Input(id='foo_{}'.format(i)),
  dcc.Input(id='bar_{}'.format(i)),
  dcc.Input(id='zam_()'.format(i))] for i in range(n)]

I’m still prototyping, but will likely have 4-5 of these. I’m starting to refactor a bit to make an object for each set of foo and bar and various functions I call based on their values. I find the callbacks to be sort of… ugly.

@app.callback(
    Output('plot', 'children'),
    [Input('button', 'n_clicks')],
    [State('foo_0', 'value'), State('foo_1', 'value'),
     State('foo_2', 'value'), State('foo_3', 'value'),
     State('bar_0', 'value'), State('bar_1', 'value'),
     State('bar_2', 'value'), State('bar_3', 'value'),
     State('zam_0', 'values'), State('zam_1', 'values'),
     State('zam_2', 'values'), State('zam_3', 'values')])
def cback(clicks,
          foo_0, foo_1, foo_2, foo_3,
          bar_0, bar_1, bar_2, bar_3,
          zam_0, zam_1, zam_2, zam_3):
    return plot

Is there a better way to do this? I think I could do the same comprehension on the State() items, right? But then what do I pass to the callback function itself without manually matching up the input/state values with variable names?

I found this question, but this isn’t exactly clear:

output_elements = ['id1', 'id2']

def create_callback(output):
     def callback(input_value):
        if output == 'id1':
            # do something
        elif output == 'id2':
            # do something different
    return callback

for output_element in output_elements:
     dynamically_generated_function = create_callback(output_element)
    app.callback(Output(output_element, '...'), [Input(...)])(dynamically_generated_function)

Wouldn’t I still be on the hook to pre-define the inputs in def callbcack(input_value) in create_callback() as well as the list of [Input()] in the app.callback?

While this isn’t a huge deal by any means, it does feel like it gets clunky when there are a lot of inputs. I’d love to define these once somewhere vs. basically passing an identical list both to app.callback and to the function itself.

Thanks for any tips/ideas!

Edit just so it’s clear, if I decide that three sets of each input is sufficient, I have to go delete all instances of var_3. If I want to add another set of inputs, I need to go add var_4 everywhere. I love the flexibility of creating dcc.Blah via list comprehension; something analogous for callbacks would be awesome.

I agree that aligning large numbers of Inputs/States with the the corresponding positional arguments within the callback function is where the Dash callback architecture starts to become a little unwieldy. I’ve outlined some thoughts about this friction point in this Dash issue. In the issue, I describe a strategy I’ve been using to both generate lists of Inputs/States and to synchronise them with metadata describing how each Input/State and value pair should be handled.

An improvement that would help avoid this friction would be enhancing the callback function signature to associate somehow with each Input/State the corresponding element ID rather than only having their values.

Store your div names in a variable list elsewhere and have your callbacks take arguments dynamically.

Or some such idiom…

Edit: realized that app.callback() assumes a type for each positional argument, and had some fun learning how list.extend([]) works

3 Likes