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/