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

Posted

技术标签:

【中文标题】如何将参数传递给函数的 __code__?【英文标题】:How to pass arguments to the __code__ of a function? 【发布时间】:2011-08-17 23:44:14 【问题描述】:

以下作品:

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

垃圾邮件

但是如果spam 接受参数呢?

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

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

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

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

我想将小的 Python 函数保存到一个文件中,以便它们可以被调用,例如从另一台计算机。 (这里不用说这个用例严格限制了可能的函数。)对函数对象本身进行酸洗是行不通的,因为这只保存了函数定义的名称和模块。相反,我可以腌制函数的__code__。当我再次解开它时,对函数的引用当然消失了,这就是我不能调用该函数的原因。我只是在运行时没有它。

另一个用例:

我在一个文件中处理多个函数,这些函数计算一些数据并将其存储在硬盘上。计算消耗大量时间,所以我不想每次都执行函数,而是只在函数的实现发生变化时执行。

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

【问题讨论】:

只要调用函数,句号,句号。 当然我会在这个简单的例子中这样做。我没那么傻没看到这个。但是如果我只有代码对象本身呢? 那你有一个更严重的问题,我会说。你很少(真的很少)需要摆弄这个抽象级别的东西,默认情况下我怀疑你是否处于这种情况,所以我建议你确保你得到一个函数而不是代码对象。 @Turion:函数和代码对象本身有什么区别?你从哪里得到这个代码对象?为什么不只是一个函数? 所以您想通过网络发送腌制函数字节码,并且您希望客户端能够只允许从某个随机位置任意执行代码?哇。这在很多方面都非常危险......你想要的是某种形式的 RPC。不要偷工减料。 【参考方案1】:

代码对象是函数的一部分,因此上面的几个答案建议创建一个虚拟函数并将其 __code__ 替换为您的 codeObject。这是另一种避免制作和丢弃新的__code__的方法:

import new
newFunction = new.function(codeObject, globals())

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

【讨论】:

这应该被标记为最正确的答案,尽管在另一个答案中的评论仍然使用那个奇怪的黑客通过types.FunctionType 提到了相同的内容(对于新读者来说,这个地方和想法很好地隐藏了)。然而,对其他答案的推荐应该被删除/不要放在首位;并且示例应该足够完整,例如new.function(spam_code_object, globals())("some eggs")。 (除了func.func_code,Python 2.7 和 2.6 还支持func.__code__【参考方案2】:

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

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

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

g(3)

【讨论】:

这与其他一些解决方案没有太大区别。 这很有趣,更准确地说,您也可以提供默认参数以防万一。将此作为关键字参数添加到构造函数argdefs = f.__defaults__【参考方案3】:

我认为您不能将参数传递给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"

【讨论】:

【参考方案4】:

我认为在您的大型应用程序中可能有一些设计考虑因素可能会让您不关心这个问题,比如可能有一些“已知良好且有效”的函数集合作为执行代理知道的模块分发或其他东西.

也就是说,一个 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
>>> 

【讨论】:

我喜欢这个,就像 Gustav Larssons 的解决方案。【参考方案5】:

我完全反对使用 __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__

【讨论】:

我也喜欢这个解决方案,但不幸的是,我认为我只能勾选其中一个解决方案。但我同意,这真的很hacky。 也可以使用函数类型构造函数。例如new_func = type(lambda: 0)(code, globals()) 这有点不那么老套了。然后,您可以编写一个函数来从代码对象构造一个包装函数并调用生成的函数。 这是一个改进。为了提高解决方案的可读性,我建议使用types.FunctionType 而不是type(lambda: 0)【参考方案6】:

你能把函数改成接受任何参数吗?然后从您可以提供到exec 的本地/全局变量中查找变量:

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

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

【讨论】:

将此与 ***.com/a/33112180/5509575 结合起来,我认为您有一个完全有效的答案,尽管我无法获得链接的答案来做任何事情

以上是关于如何将参数传递给函数的 __code__?的主要内容,如果未能解决你的问题,请参考以下文章

如何将附加参数传递给自定义 python 排序函数

如何将参数传递给 Python 模块中的主函数?

在LUA中-在_G中找到了“函数”类型的变量,如何将参数传递给它

如何将类方法作为参数传递给该类外部的函数?

Python:如何将参数传递给线程作业(回调)

当我将参数传递给类构造函数时,是否需要其他名称相同或自已的函数可用?