Newbie finds naming snobbish

Some observations from a new user, for what they’re worth.

The app callback functions are, how do I put it, snobbish? Specifically, the variable/parameter “input_value” Here’s what I have to start with:

@app.callback(
    Output(component_id='my-div', component_property='children'),
    [Input(component_id='my-id', component_property='value')]
)
def update_output_div(input_value):
    return 'You\'ve entered "{}"'.format(input_value)

Don’t we always expect a variable to be declared somewhere so we always understand context? I mean this broadly, not just in programming. If I wanted you to bring me something from the kitchen table I wouldn’t make up a word and say, bring me the “tokoticoat”. I’d point to the item and say, bring me the “honey”. Or, I might just say “bring me what’s on the table” so you know the item is a dummy variable of the table.

In the above sample code, “input_value” seems to be a real name, like “honey”. But I don’t see it declared anywhere. I feel stupid, what does everyone else know about “input_value”?

Then I noticed that if I put in “tokoticoat” it works the same. Why, because the function under the callback takes whatever was put into the input. It’s a parameter name you call whatever you want. I’m new to this, am I wrong? Philosophically, should anything be named that we have no control over?

A better variable would just be an underscore say, so

@app.callback(
    Output(component_id='my-div', component_property='children'),
    [Input(component_id='my-id', component_property='value')]
)
def update_output_div(_):
    return 'You\'ve entered "{}"'.format(_)

Or maybe “dash_callback_input”

Declarative programming can be very powerful. It can also be inscrutable. I’m finding it very frustrating and feel some of it can be avoided.

Again, just my observations before I learn this stuff and move on. If you want more Dash adoption, I believe you need to be kinder to a new person’s naming expectations. Back to having some fun!

This variable can be named anything. It is simply the parameter of the function and therefore isn’t declared anywhere else. How Dash works behind the scenes is that @app.callback decorator “registers” the input and outputs to the function and then Dash will “call” your function (update_output_div) passing in the inputs in positional order. So, update_output_div can be named anything you want and the input arguments of your function can be named anything you want. The important thing is the order of the arguments: it should match the order of the [Input(...), ] that you supply in the @app.callback decorator.

If you are interested in learning more about the @app.callback syntax, I highly recommend crunching through this explanation on decorators: https://stackoverflow.com/questions/739654/how-to-make-a-chain-of-function-decorators/1594484#1594484. This is how I originally learned about them years ago!

I am interested. And I am reading everything I can find. If you’re interested in how a new person reacts to your marvelous work then you should read what I wrote again. Not everyone will “crunch” through the explanations. I will. I’m just trying to help you gain more adoption. And to do that, I believe you need to be more critical of unnecessary confusion you’re causing by not working harder to make your variables and sample code readable. You’re just digging in and basically saying the problem is with me because I haven’t read the documentation (which I have).

I couldn’t do what you’ve done. It’s rocket science to me. Again, up to you what IQ level you want to make your cut-off at :wink: I will try to make the cut! Just wanted to let you know it won’t be easy for me. Thanks!

One last thing, question for you, what rules do you follow to call a variable which the user has no control over and which the name is arbitrary? You used “input_value” in your demo, I’ve seen others use “value” It’s not a question of if it works or not. It does. The question is how much time do you want a new user to spend trying to figure things out what’s going on? That’s my main point. And as I work on it I will try to share what I’ve learned with others. I just felt I should tell you and others about this main thing that seems to be confusing a lot of people. Variable names. Variable scope.

Oh and thanks for the feedback. Will go read now!

Okay, I’ve read it. Still trying to digest it. But my first takeaway is that decorators reduce the number of lines of code you need to write but with a SEVERE comprehension penalty. I’m very rusty with this programming stuff but I don’t see how a simple on_change event would suffice here and make the code much more readable. Again, I’m new to this. The more I look into this, the more I believe DASH is only the top 1% of programmers. I believe there’s a thing as TOO Pythonic :wink: I’ll keep trying.

Thanks for your feedback @maxrottersman

You’re welcome! This stuff is wicked cool!

I’d like to politely disagree that Dash is only for the top 1% of programmers. I haven’t done any coding since I was in college in the 80’s (pre internet!) and when I found Dash I thought it was so cool, it inspired me to learn Python. Sure, I had to learn the basics, but I had my app live within a couple of weeks. It’s an amazing product!

2 Likes

Thanks for your comment! Though, if you coded in the pre-internet you’re probably in that 1% :wink: I agree, it is an amazing product!!!

I’m thinking about this thread again and how we could improve the docs. Would love some feedback from folks re addressing the OP’s concerns.

Let’s consider:

@app.callback(Output('my-div', 'children'), Input('my-id', 'value'))
def update_output_div(input_value):
    return 'You\'ve entered "{}"'.format(input_value)

What do we name my-div & input_value?


Re Meaningful Variable Names

In practice, I’d recommend using meaningful variable names. Similar to what @maxrottersman pointed out re “honey”. In a real app, this might be “voltage” or “price”. In our documentation, perhaps we update this to reflect something about the dataset? Like “city” if the dropdown represents cities. What about about this dummy example that’s just a text input? There isn’t anything meaningful here except literally “this is the value of the input” (input_value makes sense then). Do we make up a fake meaning here like “search_term”? That seems more confusing to me than input_value. Or do we refactor this example to have more real world data? I’d prefer to keep these examples as simple as possible.

One problem with meaningful variable names is that they become less ammenable to copy-and-paste. If we use neutral names like value then users can easily copy-and-paste the examples and adapt them to their own data. If we use city or voltage everywhere, then it’s another thing to replace.


Re _

The convention for _ is to be used for unused varibles. So, I think we’d be going against convention here.


value

We could name this value, but I like to avoid “coincidental naming”. If name the input argument value then it matches the property name of the component (value). But it doesn’t have to and as mentioned above (“Meaningful Variable Names”) it shouldn’t in real apps. If we kept value, then users might think that property names need to match function argument names which isn’t true.


*args

@app.callback(Output('my-div', 'children'), Input('my-id', 'value'))
def update_output_div(*args):
    return 'You\'ve entered "{}"'.format(args[0])

I think *args is too advanced of a concept to introduce. Also it might enforce bad patterns (dereference large lists of args or referencing an unscrupulous arg[11] in the code)


**kwargs

What if we set the keyword arguments of the function? They could be like <id>.<property>. So, this example would be:

@app.callback(Output('my-div', 'children'), Input('my-id', 'value'))
def update_output_div(**kwargs):
    return 'You\'ve entered "{}"'.format(kwargs['my-id.value'])

I think we could do this in a backwards compatible way. I actually like this idea - it reduces the amount of cognition required to name variables. Basically you only need to worry about naming the IDs now.

**kwargs is an advanced concept and a difficult / magical word (“keyword arguments”). **inputs would be more meangiful but becomes confusing with Input vs State. **callback_arguments? That’s too long. **params? That’s not bad :thinking:

This is a lot nicer for functions with a large number of input arguments. I’ve written a few apps that had 10-15 input arguments and it was tough & tedious to keep the order of Input / State matched up with the input argument names.

I think we could do this in a backwards compatible way.


<id>_<property>

Similar to value and kwargs['my-id.value'], maybe my_id_value?

@app.callback(Output('my-div', 'children'), Input('my-id', 'value'))
def update_output_div(my_id_value):
    return 'You\'ve entered "{}"'.format(my_id_value)

I have simliar concerns to value & “coincidental naming”. I don’t want users to think that their input argument names must be <id>_<property>.replace('-', '_')


ID Naming

A similar problem exists for naming the IDs. I’ve tried to use things like my-dropdown instead of just dropdown to prevent the “coincidental naming” problem. However, perhaps we use more meaningful / data-relevant variable names.


Anyone else have any thoughts here?

1 Like

Glad you’re coming back to this topic. I haven’t used Dash for quite a while but hope to come back to it. What about my last suggestion, something like “plotly_input_value”, or something that indicates that the variable wasn’t previously declared? Or maybe just a comment in the sample code?

Here is the Medium article I wrote at the time trying to sum up my experience.

Anyway, now that I’ve forgotten Dash perhaps I can be a guinea pig again :wink: Another huge headache I had at the time is hosting my projects on web servers. Anyway, looking forward to getting back into it again!

1 Like

Yeah, actually that’s not a bad idea. It’s self descriptive and doesn’t suffer from “coincidental naming”. It’s long enough that it feels self descriptive and self descriptive enough that it might not need a comment.

We could use that for the examples that don’t have a “meaningful” variable name like city (or honey :wink:)

Now what would we do for multiple inputs? dash_callback_input_1, dash_callback_input_2?

Perhaps just a section on the tradeoffs of variable naming for clarity/understandability vs variable naming for ease of copypasta/transferability?

1 Like

I really like the the way to do it with **kwargs. It’s intuitive and easy to implement, even when there are decorators with many inputs.

1 Like

Yes, this is a good topic.

When I first started with Dash, I found the examples with more specific names and data sets to be much more meaningful and easier to copy/past and adapt to my use case. It took me a while to understand the really minimal examples and “get” how to use them.

BTW, I think the same is true for the pattern matching callbacks section in the documentation. I didn’t get that documentation until I experimented with the sample app that was included in the forum announcement for this feature. (I’d like to see that app included in the docs)

I agree that the name shouldn’t be “value” or “figure” or any of the names of the properties. I’ve seen people confused by this in forum posts.

*args and **kargs would be a great option. Anyone designing an app as complex as having 10-15 input arguments would likely know about *args and *kargs, so this wouldn’t be an “advanced” concept.

3 Likes

Both Adam and AnnMaria seem to like *args. I believe those are good too. Sorry to sound like a dummy, but if I saw those I would have just thought “comes from somewhere else so leave alone and research the function some more”. My problem is that when it didn’t work I started looking for “input_value”. My solution of “dash_input_value” would also create a specificity problem you pointed out.

Just to throw more fuel on the fire… in the examples above the decorated callback is named update_output_div but this name is essentially useless today for purposes other than IDE/editor “jump to function” or “find in file” features because we don’t actually decorate the function: we use Python decorator syntax to register the function with the app.

So there’s a case to be made for "always use def callback(...)" (in the docs that is!) to reduce confusion/cognitive load.

There’s also a case to be made for "always use def update_<describe output values>(...)" for findability/consistency.

Thoughts?

2 Likes

I agree here absolutely. As a beginner I wish there were more meaningful names to get the picture of where to put what more easy. I don’t care about some more work renaming then. It will still be quicker than sitting here and starring or trying to understand.

Also with find/replace functionality it’s not too much work. *args and **kargs, I don’t know much about yet and where and how to use ideally and why. Because I’m learning both, dash and python from the beginning. So, not sure if that would be ideal.

Maybe there could be universal examples and then small examples with meaningful names, showing a little variation of use maybe to even cover more topics then. My 2c

2 Likes

Good comments!

For the first minimal example in the callback section - would something like this be easier to follow?

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

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
    dcc.Dropdown(
        id='city-dropdown',
        options=[
            {'label': 'New York City', 'value': 'NYC'},
            {'label': 'Montreal', 'value': 'MTL'},
            {'label': 'San Francisco', 'value': 'SF'}
        ],
        value='NYC'
    ),
    html.Div(id='output-div')
])


@app.callback(Output('output-div', 'children'), Input('city-dropdown', 'value'))
def update_output-div(city):
    return f'You have selected {city}'


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

Yes, giving this context as city-dropdown and city provides more clarity (to me).

Also … let’s not forget that python allows comments in the code itself !!

IMO … all of these code samples can likely be improved with a few judicious comments at the appropriate locations

@app.callback(Output('output-div', 'children'), Input('city-dropdown', 'value'))
def update_output-div(city):
    return f'You have selected {city}'
# Learner tip : the input argument can be named anything ... there just needs to be consistency within the update_output-div() method
2 Likes

yes, that is helpful. thx