[Python-ideas] Idea: Deferred Default Arguments?

Peter O'Connor peter.ed.oconnor at gmail.com
Fri Jul 20 10:43:56 EDT 2018


Ah, right, the fix_it(fcn) is a nice idea.  It might also be a good idea,
if we're making an external library anyway, to have a "deferred" object to
avoid overloading "None" (which may mean something else than "differ
argument").  I implemented the decorator here
<https://github.com/petered/peters_example_code/blob/master/peters_example_code/deferral.py>,
and it can be used as:

    from deferral import deferrable_args, deferred

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

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

    def main_function(a=deferred, b=deferred, c=deferred, d=deferred,
e=deferred, f=deferred):
        return subfunction_1(a=a, b=b, c=c) + subfunction_2(d=d, e=e, f=f)

    assert main_function() == (2+3*4)+(5*6+7)
    assert main_function(a=8) == (8+3*4)+(5*6+7)

I still think it would be nice to have this as a built-in python feature,
for a few reasons:
- When using non-differable functions (say in other codebases), we have to
do a bunch of "func = deferrable_args(func)" at the top of the module (or
we can just do them at runtime, but then we're doing inspection every time,
which is probably slow).
- It adds a layer to the call stack for every deferrable function you're in.
- To avoid annoying errors where you've defined an arg as deferred but
forgot to wrap the function in question.


On Fri, Jul 20, 2018 at 3:39 PM, Jonathan Fine <jfine2358 at gmail.com> wrote:

> Hi Peter
>
> You make the very good point, that
>
> > subfunction_1 may be written by someone totally different from the
> author of
> > main_function, and may even be in a different codebase.  For the author
> of
> > subfunction_1, it makes no sense to use the "None" approach instead of
> > python's normal default mechanism (since all arguments here are
> immutables).
>
> Good point. To rephrase, what should we do if we want to use a third
> party or legacy function, which begins
> ===
> def fn(a=1, b=2, c=3):
>   # function body
> ===
>
> We can solve this by defining a function decorator. Suppose we have a
> function fix_it, whose argument and return value are both functions.
> The basic specification of fix_it is that
> ---
> fixed_fn = fix_it(fn)
> ---
> is in practice equivalent to
> ---
> def fixed_fn(a=None, b=None, c=None):
>     if a is None: a = 1
>     if b is None: b = 2
>     if c is None: c = 3
>     # function body for fn
>     # or if you prefer
>     return fn(a, b, c)
> ---
>
> An aside. We can code fix_it by using
> https://docs.python.org/3/library/inspect.html
> ===
> >>> import inspect
> >>> def fn(a=1, b=2, c=3): pass
> ...
> >>> str(inspect.signature(fn))
> '(a=1, b=2, c=3)'
> ===
>
> You could also use with new code, like so:
> ---
> @fix_it
> def fn(a=1, b=2, c=3):
>   # function body
> ---
>
> I think this helps solve your problem. Is there, Peter, anything else
> that would be left to do (except, of course, write the fix_it
> function).
>
> Thank you again for your problem and comments.
>
> --
> Jonathan
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180720/34c86253/attachment.html>


More information about the Python-ideas mailing list