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, 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@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