[Python-ideas] Idea: Deferred Default Arguments?

Chris Angelico rosuav at gmail.com
Fri Jul 20 05:27:29 EDT 2018


On Fri, Jul 20, 2018 at 7:03 PM, Peter O'Connor
<peter.ed.oconnor at gmail.com> wrote:
> Often when programming I run into a situation where it would be nice to have
> "deferred defaults".  Here is an example of what I mean:
>
>     def subfunction_1(a=2, b=3, c=4):
>         return a+b*c
>
>     def subfunction_2(d=5, e=6, f=7):
>         return d*e+f
>
>     def main_function(a=2, b=3, c=4, d=5, e=6, f=7):
>         return subfunction_1(a=a, b=b, c=c) + subfunction_2(d=d, e=e, f=f)
>
> Here you can see that I had to redefine the defaults in the main_function.
> In larger codebases, I find bugs often arise because defaults are defined in
> multiple places, and somebody changes them in a lower-level function but
> fails to realize that they are still defined differently in a higher
> function.
>
> The only way I currently see to achieve this is not very nice at all, and
> completely obfuscates the signature of the function:
>
>     def main_function(**kwargs):
>         return subfunction_1(**{k: v for k, v in kwargs.items() if k in
> ['a', 'b', 'c']})
>                + subfunction_2(**{k: v for k, v in kwargs.items() if k in
> ['d', 'e', 'f']})

Hmm. This might be something where a bit of a helper could, well,
help. Is this a pattern that you make use of a lot? Suppose you write
your main function like this:

@combines(subfunction_1, subfunction_2)
def main_function(sf1, sf2):
    return subfunction_1(**sf1) + subfunction_2(**sf2)

or even like this:

@precombine
def main_function(subfunction_1, subfunction_2):
    return subfunction_1 + subfunction_2

The decorator could then generate a new function, with a proper
signature (available to help()), that hides the messy details of
filtering kwargs - and in the case of precombine, actually calls the
function as well. (Obviously you can't use this if you might want to
call a subfunction conditionally.)

Another option would be to have all your subfunctions absorb and
ignore all unexpected arguments.

    def subfunction_1(a=2, b=3, c=4, **_):
        return a+b*c

    def subfunction_2(d=5, e=6, f=7, **_):
        return d*e+f

    def main_function(**kw):
        return subfunction_1(**kw) + subfunction_2(**kw)

This doesn't help with the signature, but it's an easy thing to do.
You'd have to document your parameters separately from your functions;
appropriate for something like the subprocess module (where a bunch of
functions basically pipe their args straight into Popen), not
appropriate for a lot of other places.

On the plus side, both of these ideas work with existing Python
versions, so you wouldn't have to wait for 3.8 or 3.9 :)

ChrisA


More information about the Python-ideas mailing list