On Thu, Dec 2, 2021 at 6:59 PM Brendan Barnwell <brenb...@brenbarn.net> wrote: > > On 2021-12-01 23:36, Chris Angelico wrote: > > That's exactly why it's such a close parallel. The late-evaluated > > default is just code, nothing else. It's not "stored" in any way - it > > is evaluated as part of the function beginning execution. > > But it IS stored! There is no way for it to be evaluated without it > being stored! > > I know we're talking past each other here but it is quite obvious that > something has to be stored if it is going to be evaluated later. You > can say that it is "just code" but that doesn't change the fact that > that code has to be stored. You can say that it is just prepended to > the function body but that's still storing it. That is still not > parallel to a ternary operator in which no part of the expression is > EVER re-executed unless control flow causes execution to return to that > same source code line and re-execute it as a whole.
I'm not sure I understand you here. How is the late-bound default "stored" when one side of a ternary is "not stored"? > Actually this raises a question that maybe was answered in the earlier > thread but if so I forgot: if a function has a late-bound default, will > the code to evaluate it be stored as part of the function's code object? > Yes. To be precise, it is part of the code object's co_code attribute - the bytecode (or wordcode if you prefer) of the function. Here's how a ternary if looks: >>> def f(n): ... return 0 if n == 0 else 42/n ... >>> dis.dis(f) 2 0 LOAD_FAST 0 (n) 2 LOAD_CONST 1 (0) 4 COMPARE_OP 2 (==) 6 POP_JUMP_IF_FALSE 6 (to 12) 8 LOAD_CONST 1 (0) 10 RETURN_VALUE >> 12 LOAD_CONST 2 (42) 14 LOAD_FAST 0 (n) 16 BINARY_TRUE_DIVIDE 18 RETURN_VALUE The "42/n" part is stored in f.__code__.co_code as the part that says "LOAD_CONST 42, LOAD_FAST n, BINARY_TRUE_DIVIDE". It's not an object. It's just code - three instructions. Here's how (in the reference implementation - everything is subject to change) a late-bound default looks: >>> def f(x=>[]): print(x) ... >>> dis.dis(f) 1 0 QUERY_FAST 0 (x) 2 POP_JUMP_IF_TRUE 4 (to 8) 4 BUILD_LIST 0 6 STORE_FAST 0 (x) >> 8 LOAD_GLOBAL 0 (print) 10 LOAD_FAST 0 (x) 12 CALL_FUNCTION 1 14 POP_TOP 16 LOAD_CONST 0 (None) 18 RETURN_VALUE The "=>[]" part is stored in f.__code__.co_code as the part that says "QUERY_FAST x, and if false, BUILD_LIST, STORE_FAST x". It's not an object. It's four instructions in the bytecode. In both cases, no part of the expression is ever re-executed. I'm not understanding the distinction here. Can you explain further please? 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/7QV4GCAMP65H77W7RM732IHNOCPHOQSW/ Code of Conduct: http://python.org/psf/codeofconduct/