On Tue, Oct 26, 2021 at 12:40 PM <2qdxy4rzwzuui...@potatochowder.com> wrote:
>
> On 2021-10-26 at 12:12:47 +1100,
> Chris Angelico <ros...@gmail.com> wrote:
>
> > On Tue, Oct 26, 2021 at 11:44 AM Rob Cliffe via Python-ideas
> > <python-ideas@python.org> wrote:
> > > I prefer 1).  Easier to understand and debug in examples with 
> > > side-effects such as
> > >     def f(a := enter_codes(), b = assign_targets(), c := 
> > > unlock_missiles(), d = FIRE()):
> > > (not that this is something to be particularly encouraged).
> > >
> >
> > It's worth noting that this would call the functions at different
> > times; assign_targets and FIRE would be called when the function is
> > defined, despite not entering the codes and unlocking the missiles
> > until you actually call f().
>
> So much for evaluating default values from left to right; this could be
> trouble even if the functions themsevles don't have side effects, but
> merely access data that has been mutated between function definition
> time and function call time.  Requiring that all late bound defaults
> come after all early bound defaults (which has already come up as a
> possibility) seems like a reasonable solution.
>
> > The difference between early evaluation and late evaluation is that
> > one retains the *value* and the other retains the *expression*. So
> > it's something like:
> >
> > _b_default = assign_targets(); _d_default = FIRE()
> > def f(a, b, c, d):
> >     if a is not set: a = enter_codes()
> >     if b is not set: b = _b_default
> >     if c is not set: c = unlock_missiles()
> >     if d is not set: d = _d_default
>
> Is the phrase/concept "retains the expression" new?  Unless it's really
> a new concept, is there an existing way to say that?

It's sloppy terminology, so the expression is probably new. You'll
find similar phenomena in lambda functions, comprehensions, and the
like, where you provide an expression that gets evaluated later; but
nothing's really "retained", it's really just that the code is run at
a particular time.

The compiler looks over all of the source code and turns it into
something runnable (in the case of CPython, that's bytecode). The code
for early-evaluated defaults is part of the execution of the "def"
statement; late-evaluated defaults is part of the call to the function
itself. Here's an example:

def f():
    print("Start")
    def g(x=q()):
        print("Inside")
    print("Done")

And here's the disassembly, with my annotations:

>>> dis.dis(f)
  2           0 LOAD_GLOBAL              0 (print)
              2 LOAD_CONST               1 ('Start')
              4 CALL_FUNCTION            1
              6 POP_TOP

-- here we start defining the function --

  3           8 LOAD_GLOBAL              1 (q)
             10 CALL_FUNCTION            0
             12 BUILD_TUPLE              1
-- default args are stored in a tuple --
             14 LOAD_CONST               2 (<code object g at
0x7f7bdb4098b0, file "<stdin>", line 3>)
             16 MAKE_FUNCTION            1 (defaults)
             18 STORE_FAST               0 (g)

-- the code is already compiled, so it just attaches the defaults to
the existing code object --

  5          20 LOAD_GLOBAL              0 (print)
             22 LOAD_CONST               3 ('Done')
             24 CALL_FUNCTION            1
             26 POP_TOP
             28 LOAD_CONST               0 (None)
             30 RETURN_VALUE

-- this is the body of g() --

Disassembly of <code object g at 0x7f7bdb4098b0, file "<stdin>", line 3>:
  4           0 LOAD_GLOBAL              0 (print)
              2 LOAD_CONST               1 ('Inside')
              4 CALL_FUNCTION            1
              6 POP_TOP
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE

-- by the time we get into this block of code, args have already been set --

Late-evaluated defaults would slip in just before the print("Inside")
line. Technically there's no "expression" that gets "retained", since
it's just a matter of where the bytecode gets placed; but in terms of
explaining it usefully, the sloppy description is far easier to grok
than a detailed look at bytecode - plus, the bytecode is
implementation-specific, and not mandated by the language.

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/WGH7E36BVFHNXGSSZO3QAVQICATU57JG/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to