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.
ChrisA
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)
print(result)
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)
(1) seems less controversial than (2).