I have a suggestion for the implementation.
I think that Chris' current approach is to compile the late-bound
defaults in the function body. So if we have a function like this:
def func(a, b=early_expression, @c=late_expression):
block
the function code looks like this (after compilation):
# Pseudocode
if c is unbound:
c = late_expression
block
(except in bytecode of course).
Chris, do I have that right? If it is wrong, probably everything I say
next is irrelevant.
There is a strange asymmetry to the way the default for b and c are
handled. For b, it is the interpreter's responsibilty to load the
default value (pre-evaluated and cached) and bind it to the parameter.
But for c, it is the function object's responsibility.
I'd like to suggest a different approach which I expect will be more
flexible, I hope won't cost too much in performance, and in my opinion
much more closely matches the semantics of the feature.
I think it should remain the interpreter's responsibility to set up all
the parameters before entering the function, including late-bound
defaults. That will have the big advantage that disassembling func will
only show the code for "block", not the associated code that tests and
binds late-bound defaults.
(Just like currently, it doesn't show the code for binding early-bound
defaults.)
This suggests that each late-bound expression should be compiled into a
separate code object, all of which are then squirrelled away in the
function object (just as the __code__ and __defaults__ currently are).
I imagine the process will be something like:
* set up a new local namespace for the function call
* bind arguments to parameters in that namespace
* bind early-bound defaults from the function __defaults__
to parameters
* (NEW) evaluate the appropriate late-bound expression code
objects, running them in the local namespace, and binding
their results to the parameters;
* enter the function's code block.
Benefits:
- the function code block is smaller, since it no longer has to
include the "test, evaluate, bind" for every late-bound parameter;
- this may improve code locality, which is good for performance
(or so I am told);
- introspection tools such as dis can disassemble the body of the
function independently of the late-bound parameters;
- which means we can inspect the late-bound parameters independently
by passing their code object to dis;
- for testing, we can evaluate the expression code objects using
eval (maybe?);
- we may be able to include the source code to the expression in
the expression's own code block, e.g. in the co_name field(?);
- we may be able to replace/modify the defaults' code blocks
independently of the main function __code__, e.g. for byte-code
hacking, or other function object hacking.
Costs:
- the function object itself may be a little larger.
Thoughts?
--
Steve
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/NQCVLVYH7PEPXVK3AIFUU5Y7XXZM2KOK/
Code of Conduct: http://python.org/psf/codeofconduct/