On Sun, Nov 14, 2021 at 2:50 PM Chris Angelico <rosuav@gmail.com> wrote:

spam(*(1,) * use_eggs)
spam(**{"eggs": 1} if use_eggs else {})

Still clunky, but legal, and guaranteed to work in all Python
versions. It's not something I've needed often enough to want
dedicated syntax for, though.


The thing is that I find myself dealing with duplicated defaults all the time - and I don't know a good way to solve the problem.  The "**{"eggs": 1} if use_eggs else {}" is obviously problematic because
* It is immune to type-inspection, pylint, mypy, IDE-assisted-refactoring, etc,
* If trying to pass down the argument it actually looks like spam(**{"eggs": kwargs["eggs"]} if "eggs" in kwargs else {}) which is even messier

A lot of the time, my code looks like this: 

    def main_demo_func_with_primative_args(path: str, model_name: str, param1: float=3.5, n_iter: float=7): 
        obj = BuildMyObject(
            model_name = model_name, 
            sub_object = MySubObject(param1=param1, n_iter=n_iter)
        for frame in iter_frames(path): 
            result = obj.do_something(frame)

ie I have a main function with a list of arguments that are distributed to build objects and call functions.

The problem is I am always dealing with duplicated defaults (between this main function and the default values on the objects).  You define a default, duplicate it somewhere else, change the original, forget to change the duplicated, etc...

* It makes sense to define the default values in one place.
* It makes sense for this place to be on the objects which use them (ie at the lowest level)
* It makes sense to be able to modify default values from the top level function.
But the above 3 things are not compatible in current python (at least not in a clean, pythonic way!)

The only ways I know of to avoid duplication are: 
* Define the defaults as GLOBALS in the module of the called function/class and reference them from both places (not always possible as you don't necessarily control the called code).  Also not very nice because you have to define a new global for each parameter of each low-level object (a a different sort of duplication).
* Messy dict-manipulation with kwargs (see above)
* Messy and fragile default inspection using inspect module 

The only decent ways I can think of to avoid duplicated-defaults are not currently supported in Python:

1) Conditional arg passing (this proposal):
        def main_func(..., param_1: Optional[float] = None, n_iter: Optional[int] = None):
            sub_object = MySubObject(param1=param1 if param1 is not None, n_iter=n_iter if n_iter is not None)

2) Ability to cleanly reference defaults of a lower-level object: 
         def main_func(..., param_1: float=MySubObject.defaults.param1, n_iter: int=MySubObject.defaults.n_iter):
             sub_object = MySubObject(param1=param1, n_iter=n_iter)

3) "Deferred defaults"... which seem to be a bit of a Pandora's box

(1) seems less controversial than (2).