On Thu, Dec 9, 2021 at 5:52 PM Brendan Barnwell <brenb...@brenbarn.net> wrote: > > On 2021-12-08 20:55, Chris Angelico wrote: > > (H) This is true. But if the two syntaxes can be sufficiently similar, > > the cost should be low, and the feature benefit would be high. Early > > binding lets you "def f(x=x):" in a loop and capture each x as it goes > > by. Late binding lets you "def f(x=>[]):" and get a new list every > > time. Both have their places. > > (The "two syntaxes" here is referring to syntaxes for early and late > binding.) I'm actually worried about the reverse. When the two > syntaxes are similar, it will be easier to mistake one for the other.
For the most part, there won't be any confusion. If the default is a simple value, it's going to be early-bound (eg "def list.pop(index=-1):"). If it's a mutable that needs to be constructed, it'll be late-bound. The distinction will be important if it's looked up from another namespace ("def fetch(timeout=some.default.timeout):"), but even then, the distinction is whether the default is affected by future changes or not; the two forms are functionally very similar. > > x = 1/y if y else "invalid" > > > > There's no object for "1/y". Trying to create one would be a nightmare > > of subtleties, where assignment expressions would break things, > > nonlocal variable references would become tricky, etc, etc. Similarly: > > As I have stated repeatedly, ternary expressions are not parallel > because they do not have a distinct definition-time and call-time. > Please stop bringing up the ternary operator case and pretending it is > the same as a function. It is not, for reasons that I have already > explained several times. > > To try stating this in yet another way, currently if I have: > > def f(a=<some code here>) > > <some code here> must be something that evaluates to a first-class > object, and the "argument default" IS that first-class object --- not > bytecode to generate it, not some behavior that evaluates the > expression, no, the default value is itself an object. This would not > be the case for late-bound defaults under the PEP. (Rather, as I > phrased it in another post, there would not "be" a late-bound default at > all; there would just be some behavior in the function to do some stuff > when that argument isn't passed.) The VALUE is a first-class object - that's the result of evaluating the expression. With early-bound defaults, that's the only thing that gets saved - not the expression, just the resulting value. (Which can be seen if you do something like "def f(x=0x100):", which will show the default as 256.) Remember, a late-bound default is most similar to this code: def f(a=<optional>): if a was omitted: a = <some code here> And in that form, the code isn't available as a first-class object. That's why I say that it is parallel to every other partial expression in Python. Until you evaluate it, there is no first-class object representing it. (A code object comes close, but it's more than just an expression - it also depends on its context. A function requires even more context.) Suppose you wanted, for a function with an early-bound default, to see the actual expression that got you there. This can be synthesized in some cases (eg an enum with a good repr), but in the general case, how can you figure out that expression? Could you reevaluate it? Suppose you wanted to make a decorator like this: @reevaluate_defaults_every_call def f(x=[], y=func(), z=x.length + y): ... Could you, using early-bound defaults, get hold of the expressions? Are they first-class objects? ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/GVOGD5VUVZF24KUAC5UHQGAVFNVZSSIV/ Code of Conduct: http://python.org/psf/codeofconduct/