On 2011-06-18 06:03, Steven D'Aprano wrote:
Nick Coghlan wrote:
On Fri, Jun 17, 2011 at 10:12 PM, Steven D'Aprano email@example.com wrote:
You have missed a fourth option, which I have been championing: make inject an ordinary function, available from the functools module. The *implementation* of inject almost certainly will require support from the compiler, but that doesn't mean the interface should!
No, I didn't miss it, I left it out on purpose because I think messing with the runtime name lookup semantics is a terrible idea.
I think that depends on what you count as part of the runtime name lookup semantics.
Isn't changing name lookup semantics at runtime precisely what JIT compilers do? But it doesn't really matter, because that's not what I'm proposing. I'm not suggesting that the lookup semantics should be changed when the function is called. I'm saying that a new function should be created, based on the original function, with the desired semantics.
In principle, this could be as simple as:
- make a copy of the function object
- in the copy, add cells for any injected variable
- and modify the copied code object to change the appropriate
LOAD_GLOBAL opcodes to LOAD_DEREF (and similarly for rebindings).
although I dare say that in practice there'll be a certain amount of book-keeping required to make it work reliably.
If you want the injected values to also affect functions that are defined within the decorated function it gets a lot more complicated. But yes, in theory it could work.
One thing that would make it a *lot* easier to write such an "inject" function would be if we could replace the way globals are looked up to use cells as well. I am thinking of a different "kind" of cell that wouldn't hold its value itself but get it from the module globals the way LOAD_GLOBAL does today. This cell would be in the __closures__ of the function and could be replaced using a decorator like Steven proposed.
A consequence of this would be that you could optionally allow "nonlocal" to bind global names when there are no suitable nonlocal names to bind to (e.g. in a top-level function). It has always slightly bothered me that you couldn't do that, because it makes it harder to move code between levels.
As written, this would probably slow down access to globals a little bit. However I have another idea (basically a more backward-compatible variation of PEP 280) that would let us use cells or cell-like objects for almost all accesses, at the cost of changing <module>.__dict__ to be a dict *subclass*.