On Fri, Jul 20, 2018 at 1:30 PM, Jonathan Fine <jfine2...@gmail.com> wrote:
>
>
> I sort of think we've now got a reasonable answer for Peter's problem.
> What do you think, Peter? And Brice, are you happy with my
> interpretation of your deferred keyword?


I think the problem with the "None" approach (second pattern) is that it
forces the writer of the subfunction to write their defaults in a more
awkward way in anticipation of other functions which defer their defaults
to it.  Translated to the original example, it would become:

    def subfunction_1(a=None, b=None, c=None):
        if a is None: a=1
        if b is None: b=2
        if c is None: c=3
        return a+b*c

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

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

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).



On Fri, Jul 20, 2018 at 1:30 PM, Jonathan Fine <jfine2...@gmail.com> wrote:

> Excellent contributions. I'm going to try to (partially) consolidate
> what we've got.
>
> REVIEW
> =======
> I'll start by reviewing the situation regarding default arguments.
> There are two basic patterns for default arguments.
>
> The first is
> ---
> def fn(a=EXP):
>     # body of function
> ---
>
> The second is
> ---
> def fn(a=None):
>     if a is None:
>         a = EXP
>     # body of function
> ---
>
> Here, EXP is any Python expression. A fairly gotcha is to use a list,
> or some other mutable object, as EXP. This happens when you write
> ---
> def fn(a=[]):
>    # body of function
> ---
> because then EXP = '[]' which will be evaluated just once, and every
> call fn() will be using the same list! To avoid this you should use
> the second pattern. I think there may be an example of this in the
> standard Python tutorial.
>
> (An aside. You probably need the second pattern if you EXP is, say,
> ([], []). Although technically immutable, this value has mutable
> members. And can't be hashed, or use as a dictionary key or element of
> a set.)
>
> WHEN TO USE None
> =================
>
> If you want something mutable as the 'default argument' you have to
> use the second pattern. If your default argument is immutable then you
> can if you wish use the second pattern.
>
> But you don't have to use the second pattern. When you use the second
> pattern, the expression f(None) means 'create for me the default
> argument' (and raise an exception if there isn't one).
>
> Think about it. For immutable EXP, fn() is the same, whether fn is
> coded using the first pattern or the second. But the value of fn(None)
> depends very much on which pattern is used to code fn().
>
> So here's the big conclusion (drum roll):
> ===
> fn should be coded using the second pattern if we wish to pass None as
> a sentinel argument to fn.
> ===
>
> SUMMARY
> =========
> My suggestion was to use the second pattern to solve Peter O'Connor's
> original problem. It can be done now, but is a bit verbose, and looses
> useful information in help(fn).
>
> Brice Parent's suggestion was to introduce a keyword deferred, like so
> ---
> def fn(deferred a=EXP):
>     # body of function
> ---
> which I like to think of as a syntactic shorthand for the second pattern.
>
> I sort of think we've now got a reasonable answer for Peter's problem.
> What do you think, Peter? And Brice, are you happy with my
> interpretation of your deferred keyword?
>
> ---
> Jonathan
>
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to