How to pass arguments to the __code__ of a function?
The following works:
def spam():
print "spam"
exec(spam.__code__)
spam
But what if spam
takes arguments?
def spam(eggs):
print "spam and", eggs
exec(spam.__code__)
TypeError: spam() takes exactly 1 argument (0 given)
Given, that I don't have access to the function itself but only to a code object, how can I pass arguments to the code object when executing it? Is it possible with eval?
Edit: Since most readers tend not to believe in the usefulness of this, see the following use case:
I want to save small Python functions to a file so that they can be called e.g. from another computer. (Needless to say here that this usecase restricts the possible functions severely.) Pickling the function object itself can't work because this only saves the name and the module where the function is defined. Instead, I could pickle the __code__
of the function. When I unpickle it again, of course the reference to the function vanished, which is why I can't call the function. I simply don't have it at runtime.
Another usecase:
I work on several functions in one file that calculate some data and store it on the hard drive. The calculations consume a lot of time so I don't want to execute the functions every t开发者_JAVA技巧ime, but only when the implementation of the function changed.
I have a version of this running for a whole module instead of a function. It works by looking at the modification time of the file where the module is implemented in. But this is not an option if I have many functions that I don't want to separate in single files.
I am completely against this use of __code__.
Although I am a curious person, and this is what someone theoretically could do:
code # This is your code object that you want to execute
def new_func(eggs): pass
new_func.__code__ = code
new_func('eggs')
Again, I never want to see this used, ever. You might want to look into __import__
if you want to load code during run-time.
Can you change the function to not take any arguments? The variables is then looked up from the locals/globals where you can supply into exec
:
>>> def spam():
... print "spam and", eggs
...
>>> exec(spam.__code__, {'eggs':'pasta'})
spam and pasta
(Why not just send the whole function as a string? Pickle "def spam(eggs): print 'spam and', eggs"
, and exec
the string (after verification) on the other side.)
I think there are probably some design considerations in your larger application that could make you not care about this problem, like perhaps having some collection of 'known good and valid' functions distributed as a module that the executing agents know about or something.
That said, one hacky solution would be:
>>> def spam(eggs):
... print "spam and %s" % eggs
...
...
>>> spam('bacon')
spam and bacon
>>> def util():
... pass
...
...
>>> util.__code__ = spam.__code__
>>> util('bacon')
spam and bacon
>>>
A code object is part of a function, so several answers above suggest creating a dummy function and replacing its __code__
with your codeObject
. Here's another way that avoids making and throwing away a new __code__
:
import new
newFunction = new.function(codeObject, globals())
(Tested in Python 2.7, where spam.__code__
is named spam.func_code
.)
My method, I thing it`s more beautiful
def f(x):
print(x, x+1)
g = type(f)(f.__code__, globals(), "optional_name")
g(3)
I don't think you can pass arguments to either exec
or eval
, so that they are passed to the code object.
You could resort to the string version of exec/eval, e.g. exec("spam(3)")
.
You could create another code object that binds the argument, and then exec this:
def spam_with_eggs():
return spam(3)
exec(spam_with_eggs.__code__)
(I thought you could also achieve this with functools.partial
, but didn't get it to work).
EDIT:
After reading your additional explanations I thought of ways to re-establish a proper function from the code object. This simple approach worked for me (in python2.5):
def bar():pass
bar.func_code = spam.func_code
bar(3) # yields "spam and 3"
精彩评论