[Python-ideas] 'Injecting' objects as function-local constants
Eric Snow
ericsnowcurrently at gmail.com
Fri Jun 17 20:43:28 CEST 2011
On Fri, Jun 17, 2011 at 9:22 AM, Alex Light <scialexlight at gmail.com> wrote:
>
> Doing things this way however removes one of the chief benefits of the
> inject statement,
> the ability to create multiple versions of the same function with different
> sets of shared data.
The only way I could see runtime injection work is if you limited the
injection to names already tied to the locals, in the same way
parameters are. This would require one of the 3 solutions that Nick
outlined.
Let's assume the injection values were stored on the function object,
like closures and defaults are, perhaps in an attribute named
__atdef__. Then runtime injection could be used to replace __atdef__,
like you can with the defaults, if it were not read-only.
However, If __atdef__ were read-only, like __closure__, the runtime
injection would have to do some trickery, like you have to do if you
are going to mess with the closures [1]. This is a hack, in my mind,
since being read-only indicates to me that the expectation is you
shouldn't touch it!
With that said, in that case a runtime injection function would have
to generate a new function. The new function would have to have the
desired __atdef__, and match any other read-only attribute. Then the
injection function would copy into the new function object all the
remaining attributes of the old function, including the code object.
Here's an example of what I mean:
def inject(f, *args):
if len(args) != len(f.__atdef__):
raise TypeError("__atdef__ mismatch")
func = FunctionType(f.__code__, f.__globals__, f.__name__,
f.__defaults__, f.__closure__,
tuple(args))
# copy in the remaining attributes, like __doc__
return func
You can already do this with closures and defaults. If the elements
of __atdef__ are cells, like with __closure__ then you would have to
throw in a little more logic. If you wanted to do kwargs, you would
have to introspect the names corresponding to __atdef__. I don't know
if this would have a performance impact on the new function, in case
__defaults__ or __closure__ are more than just attributes on the
function object.
Like I said, this is a hack around the read-only attribute, but it
shows that with the solutions Nick outlined, you can still have an
injection function (could be used as a decorator, I suppose) . The
only catch is that the names for __atdef__ would be tied into the
function body at definition time, which I think is a good thing.
Finally, regardless of if __atdef__ were read-only or not, I think a
runtime injection function should return a new function and leave the
old one untouched. That seems to meet the use-case that you
presented.
-eric
[1] Here's an example:
from types import FunctionType
INJECTEDKEY = "injected_{}"
OUTERLINE = " outer_{0} = injected_{0}"
INNERLINE = " inner_{0} = outer_{0}"
SOURCE= ("def not_important():",
" def also_not_important():",
" return also_not_important")
def inject_closure(f, *args):
injected = {}
source = list(SOURCE)
for i in range(len(args)):
source.insert(1, OUTERLINE.format(i))
source.insert(-1, INNERLINE.format(i))
injected[INJECTEDKEY.format(i)] = args[i]
exec("\n".join(source), injected, injected)
closure = injected["not_important"]().__closure__
func = FunctionType(f.__code__, f.__globals__, f.__name__,
f.__defaults__, closure)
func.__annotations__ = f.__annotations__
func.__doc__ = f.__doc__
func.__kwdefaults__ = f.__kwdefaults__
func.__module__ = f.__module__
return func
More information about the Python-ideas
mailing list