General questions about callbacks

I have general questions about how callbacks work. As per examples on Plotly website, a common convention (or maybe correct way) of writing callbacks looks like this:

@app.callback(
    Output('graph-with-slider', 'figure'),
    Input('year-slider', 'value'))
def update_figure(selected_year):
    filtered_df = df[df.year == selected_year]

    fig = px.scatter(filtered_df, x="gdpPercap", y="lifeExp",
                     size="pop", color="continent", hover_name="country",
                     log_x=True, size_max=55)

    fig.update_layout(transition_duration=500)

    return fig

My questions are:

  1. Do I have to always define functions below callbacks? Does it matter where I place my code associated with callback?

  2. Can I save functions in another file and import them in app.py file under callback?
    For example, can the code above be written like this:

import functions

@app.callback(
    Output('graph-with-slider', 'figure'),
    Input('year-slider', 'value'))
functions.function1(selected_year)
  1. Is it possible to define a function with *args or **kwargs? I’m asking this because, when I define a function explicitly with arguments, callback expects that those arguments are not empty from the beginning. I know, that I can avoid it by adding a control like this if argument is not None:, but it is not convenient.

HI @parvizalizada

  1. as far as I know, the decorator expects a def statement so what you propose won’t work.

  2. You could wrap your imported function. I did something like this in the past. It’s maybe not the best way to do it, but it works

@app.callback(
    Output(...),
     Input(...)
)
def update(*args):
    # which input type triggered the callback?
    trigger = ctx.triggered_id['type']
    
    extended_args = [arg for arg in args]
    extended_args.append(trigger)
    
    return imported_function(*extended_args)

It’s an interesting question and maybe there are others on the forum with better ideas.

@AIMPED , thanks for your answers. Regarding the second point, I wanted to avoid defining functions explicitly (i.e., def some_function:). But if it is the way callbacks work, the code will still look complicated, keeping all these defs in one file and having many callbacks.

hi @parvizalizada ,
You can define your callback without decorations. Just like this,
app.callback()(your_func)
Or
app.callback()(lambda x: your_func(x))

1 Like

hi @stu
I’m not sure if I understand. Do you mean this is the syntax:

import functions

app.callback()(functions.function1())

no, don’t pass in the instance, just pointer.

import functions

app.callback()(functions.function1)

1 Like