Where I put default values for options in Python's argparse and why

I wrote the other day about an irritatingly incompatible change in Python 3.14's argparse , where if you deferred setting a default value until later, certain argparse things would now error out. In a comment, Ian Z aka nobrowser asked (in part):

Setting the default right in the add_argument call has always seemed more natural to me. Do you have a case where it is not possible or just inconvenient to do this?

Some of my usage of set_defaults is historical and I might do it differently today; in today's Python, setting default values in the add_argument call is clearly the accepted and expected style. However, I feel that using set_defaults for some or all settings can be a clearer choice if a bunch of default values are clearly related to each other. By setting related default values together, you give future readers visibility into what all of the defaults are, in one place, without forcing them to read the code and pick out all of the 'default=' arguments in add_argument calls.

In my view this is especially useful for quick views to remind yourself of what the code's default behavior is. Yes, you can run '--help' (if it prints default values, perhaps with ArgumentDefaultsHelpFormatter ), but that tells you a lot of additional things beyond the defaults. How much this matters depends on how many arguments you have and how they manipulate their destinations (which isn't always simple). Some of my programs are definitely ones where the default values belong with argument, in add_argument , but others have enough that I feel that set_defaults is perfectly fine and sometimes easier to deal with.

Using set_defaults also puts the name of the option and its default value next to each other, as an assignment. In an add_argument call they're split across two keywords:

   p.add_argument("--whatever", dest="whatever",
                  action="store_true",
                  default=False,
                  help="Don't do whatever.")

  # Compare to:
  p.set_defaults(whatever=False, ...)

To read the add_argument() you (I) have to combine 'dest="whatever"' with 'default=False' to get to 'whatever is False by default'. By contrast, that's right there in the set_defaults .

(Looking back and forth between various programs actually makes me feel I haven't been using set_defaults as much as I probably should. Putting the default value into each new add_argument call is the easy way as my programs evolve, but maybe I want to go back more often and set all of them in one block once enough arguments have accumulated.)

Sidebar: A slightly tricky destination manipulation

Consider the following code (which is a version of something I've done in some of my programs):

   p.add_argument("--no-whatever", dest="whatever",
                  action="store_false",
                  default=True,
                  help="Don't do whatever.")

This is a perfectly reasonable way to turn off something but it's a little bit confusing to read 'default=True' in the context of setting up the '--no-whatever' argument. In my view this sort of thing is clearer in set_defaults , where you aren't distracted by other things and it's clearer that the default value of 'whatever' is True (on).

(This trick also causes ArgumentDefaultsHelpFormatter to report the misleading '(default: True)' for '--no-whatever'.)