Conditional function/method/dict arguments assignments

This is just a hack idea I had for functions/methods that have parameters with default values, possibly outside the control of the user of said function, where sometimes it is desired to pass an argument to the function that differs from the default but other times it is desired to just use the default, whatever that may be # An external function def dosomething(count=5): doing_something # The function using an external function def dosomething_twice(count=None): # if count is none, just use the default count from dosomething if count is None: dosomething() dosomething() else: dosomething(count) dosomething(count) # or use a dict # kwargs = {} # if count is not None: # kwargs["count"] = count # dosomething(*kwargs) # dosomething(*kwargs) We could set the count's default value to 5 in dosomething_twice, but then if the external function changed its default, calling dosomething_twice with no parameters would no longer actually be using dosomething's default. If dosomething were in our control, we could set count=None as default and in the code do something like count = 5 if count is None else count. I imagine a syntax like def dosomething_twice(count=None): dosomething(count is not None ?? count) # If count is not None, pass it, else, use the dosomething's default parameter. dosomething(count is not None ?? count) This syntax is <conditional parameter test> ?? <value if test is True>. The requirements are that the parameter to be filled in in the calling method must have a default value. If the test evaluates to false, the right hand side will not be evaluated (lazy evaluation) and instead the default value will be used. This can also be used for passing by keyword arguments: def dosomething_twice(count=None): dosomething(count = count is not None ?? count) dosomething(count = count is not None ?? count) It could also be used to avoid specifying long argument lists: # external button.create(self, parent, x=0, y=0, w=64, h=32, style=...., label="", windowclass="") calling: ??, by itself, will fill in that parameter with the default. If the external code ever changes, no need to change our code to update each time. The use case here is to skip specifying a parameter in a long function parameter list but still have its default used. This probably only make sense for parameters being passed via positional arguments, since "style=??", (pass as a keyword style the default value), can just be done by omitting style call. def create_buttons(style=None): button = Button(self, 10, 10, ??, ??, style is not None ?? style, "Ok") # pass the defaults for w, h, and style if a our style parameter is None else uses the specified style, if the calling code changes the default, our code will automatically use it. button = Button(self, 110, 10, ??, ??, style is not None ?? style, "Cancel") button = Button(self, 110, 10, ??, ??, style is not None ?? style, "Retry", wiindowclass=??) # see keyword "windowclass=??" doesn't make much sense as the result is achieved by excluding it. A similar use case could possibly be made for conditional dict assignments: data = {} if count is not None: data["count"] = count Could be written as data = { "count" = count is not None ?? count, # set the "count" key in the dictionary conditionally, if count is not None, else don't set the key at all "other_param" = ... ?? ..., ... } Thanks, Brian Vanderburg II

I had many times the same idea: why can't we just "say" to the called function "use your own default"? I'm quite sure this is possible in a tricky way, since defaults are stored in the function object. Anyway, honestly I don't like your syntax proposal.

I've been toying with a similar idea myself. I've felt the pain described by Brian, and I share Marco's dislike for the suggested syntax. Moreover, I dislike the idea that the conditional should somehow refer to the function's default arguments. My half-baked idea is along the lines of f(val1, val2, if cond3: val3, if cond4: arg4=val4) with the sense that if a condition is not met, the argument is just not passed; this would be equivalent to f( val1, val2, *[_ for _ in [val3] if cond3], **{'arg4': _ for _ in [val4] if cond4} ) As presented, this would work for list and set literals as well, but the syntax is not good for dict literals; "{if cond: key: value}" feels completely wrong. Alternative "{key if cond: value}" looks palatable at first glance, but doesn't feel quite "in line" with the rest of the proposal, and for my taste, looks too similar to the already valid "{key1 if cond else key2: value}". That last similarity is also why I don't like the syntax f(arg1, arg2, arg3 if cond3, arg4=cond4 if cond4) I've been thinking about alternatives such as f(arg1, if (cond2) arg2) f(arg1, (if cond2) arg2) or just marking the if as a "special if", e.g. f(arg1, arg2 if* cond2) But I'm not really pleased with any of them. Have fun, Shai.

I wouldn't call it tricky, it's actually quite straightforward: import inspect def extract_default(function, parameter): sig = inspect.signature(function) param = sig.parameters[parameter] return param.default def do_something(count=5): print(count) def do_something_twice(count=None): if count is None: count = extract_default(do_something, "count") do_something(count) do_something(count)

That won't work with at least some builtins written in C, and maybe extension modules. I just checked 3.9 and str.count, and inspect.signature fails with ValueError: no signature found for builtin <method 'count' of 'str' objects>. I don't know if Argument Clinic (AC) would improve this, or maybe it's outside of what AC can help with. I've often wanted to call functions, including builtins, and say "just use the default", so I can see the need for a more reliable way of finding the defaults. Eric On 10/26/2020 5:25 AM, jan.thor@inter.de wrote:
I wouldn't call it tricky, it's actually quite straightforward:
import inspect
def extract_default(function, parameter): sig = inspect.signature(function) param = sig.parameters[parameter] return param.default
def do_something(count=5): print(count)
def do_something_twice(count=None): if count is None: count = extract_default(do_something, "count") do_something(count) do_something(count) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/KVEGN4... Code of Conduct: http://python.org/psf/codeofconduct/
participants (5)
-
Brian Allen Vanderburg II
-
Eric V. Smith
-
jan.thor@inter.de
-
Marco Sulla
-
Shai Berger