Python:如何将参数传递给函数的__code__?

时间:2011-05-03 19:24:41

标签: python function parameter-passing exec eval

以下作品:

def spam():
    print "spam"
exec(spam.__code__)
  

垃圾邮件

但是如果spam接受参数呢?

def spam(eggs):
    print "spam and", eggs
exec(spam.__code__)
  

TypeError:spam()只需1个参数(0给定)

鉴于,我无法访问函数本身,只能访问代码对象,如何在执行代码对象时将参数传递给代码对象?是否可以使用eval?

编辑:由于大多数读者往往不相信这有用,请参阅以下用例:

我想将小型Python函数保存到文件中,以便可以调用它们,例如从另一台电脑。 (这里不用说这个用例严格限制了可能的功能。)pickle函数对象本身不起作用,因为这只保存了定义函数的名称和模块。相反,我可以挑选函数的__code__。当我再次取消它时,当然对函数的引用消失了,这就是我无法调用该函数的原因。我只是在运行时没有它。

另一个用例:

我在一个文件中处理几个函数,计算一些数据并将其存储在硬盘上。计算会耗费大量时间,因此我不希望每次都执行这些函数,但只有在函数的实现发生变化时才会执行。

我有一个版本的运行整个模块而不是一个函数。它通过查看实现模块的文件的修改时间来工作。但如果我有许多我不想在单个文件中分离的函数,那么这不是一个选项。

6 个答案:

答案 0 :(得分:15)

我完全反对使用__code __。

虽然我是一个好奇的人,这是理论上可以做的事情:

code # This is your code object that you want to execute

def new_func(eggs): pass
new_func.__code__ = code
new_func('eggs')

同样,我永远不想看到这个用过。如果要在运行时加载代码,可能需要查看__import__

答案 1 :(得分:9)

您可以将功能更改为而不是采取任何参数吗?然后从locals / globals中查找变量,您可以将其提供给exec

>>> def spam():
...   print "spam and", eggs
... 
>>> exec(spam.__code__, {'eggs':'pasta'})
spam and pasta

(为什么不将整个函数作为字符串发送?Pickle "def spam(eggs): print 'spam and', eggs"exec另一边的字符串(验证后)。)

答案 2 :(得分:6)

我认为在您的大型应用程序中可能存在一些设计注意事项,这些注意事项可能会使您不关心此问题,例如可能将一些“已知良好且有效”的函数集合作为执行代理知道的模块或某些内容进行分发。

那就是说,一个hacky解决方案是:

>>> 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
>>> 

答案 3 :(得分:5)

代码对象是函数的一部分,因此上面的几个答案建议创建一个虚函数并用__code__替换它codeObject。这是避免制作和丢弃新__code__

的另一种方法
import new
newFunction = new.function(codeObject, globals())

(在Python 2.7中测试,其中spam.__code__被命名为spam.func_code。)

答案 4 :(得分:1)

我认为您不能将参数传递给execeval,以便将它们传递给代码对象。

您可以使用exec / eval的字符串版本,例如exec("spam(3)")

您可以创建另一个绑定参数的代码对象,然后执行以下命令:

def spam_with_eggs():
   return spam(3)
exec(spam_with_eggs.__code__)

(我认为你也可以用functools.partial实现这个目标,但是没有让它发挥作用。)

编辑:

在阅读了您的其他解释之后,我想到了如何从代码对象重新建立正确的函数。这个简单的方法对我有用(在python2.5中):

def bar():pass
bar.func_code = spam.func_code
bar(3)  # yields "spam and 3"

答案 5 :(得分:1)

我的方法,我觉得它更漂亮

def f(x):
    print(x, x+1)

g = type(f)(f.__code__, globals(), "optional_name")

g(3)