如何将函数定义作为字符串传递给Python脚本

3
我想将函数定义传递给Python命令行脚本。最好的方式是什么?我正在使用Python 2。假设我有这样一个脚本:
#myscript.py
x = load_some_data()
my_function = load_function_definition_from_command_line()
print my_function(x)

我希望这样调用它:python myscript.py 'def fun(x): return len(x)'

如何执行load_function_definition_from_command_line部分?

我想到了一种解决方法:

  1. 从命令行获取字符串函数定义
  2. 在某个临时目录中将其写入扩展名为.py的文件中
  3. 使用此问题的解决方案加载文件中的定义:How to import a module given the full path?
  4. 执行
  5. 清理

但我确信一定有更好的方法。


1
我认为你应该解释一下为什么想要将函数作为字符串传递给get ran。这可能有助于我们更好地帮助你,并可能找到避免使用当前答案所建议的方法(eval和exec)的解决方法。评估命令行参数并将其作为代码运行是极其危险的,除非绝对必要,否则不应该这样做。 - disflux
只是出于好奇 - 你为什么需要这个?我问这个问题是因为它会导致非常糟糕的解决方案(eval),也许你只是没有看到其他可能性。 - Jan Spurny
我正在编写一个 shell 应用程序,用于对其他应用程序的日志/输出进行映射/减少/过滤操作。 - Jan Grz
4个回答

2
您可以使用eval运行在字符串中定义的代码。像这样:
import sys

x = load_some_data()
function = eval("".join(sys.argv[1:]))
print(function(x))

针对你的具体示例,你可能需要使用类似于lambda x: len(x)这样的东西。

正如@Jan-Spurny所指出的:“除非你绝对确定没有其他方法,否则永远不要使用eval。即使这样,你也应该停下来再想一想。”

在我看来,更好的策略是将数据加载器和执行器转化为一个模块,其中包含一个以函数作为参数并运行所需代码的方法。最终结果可能如下:

import data_loader_and_executor

def function(x):
    return len(x)

data_loader_and_executor.run(function)

1
我强烈建议将最后一句话(“理想情况下,除非你绝对确定没有其他方法,否则永远不要使用eval。”)更加明显(加粗)或重新表述,例如:“永远不要使用eval,除非你绝对确定没有其他途径。即使如此,你也应该停下来再次思考。” - Jan Spurny
eval的危险在哪里? - Jan Grz
2
@JanOsch - 有人可以传递任何东西并执行它。例如,某些开玩笑的人可以传递:"def f(x): import shutil; shutil.rmtree('/');" 并删除您的文件系统。Eval是您的程序中最大、最危险的后门。 - Jan Spurny

0
使用函数exec

import sys

def load_function_definition_from_command_line():
   exec(sys.argv[1])
   return locals()['fun']

当然,你必须知道你的函数将被命名为什么,但这可以通过将第二个参数传递给你的参数来完成:

 $ python myscript.py 'def fun(x): return len(x)' fun

然后你的函数将会是这样:

import sys

def load_function_definition_from_command_line():
   exec(sys.argv[1])
   return locals()[sys.argv[2]]

记住,评估用户输入非常危险!

编辑:由于fun将是locals中唯一定义的对象,因此您可以只返回locals()中的第一个元素:

def load_function_definition_from_command_line():
   exec(sys.argv[1])
   return locals()[0]

0

你可以使用 eval 或 exec 在当前命名空间中创建一个函数。

exec "somefunc(x): return x * 2"
somefunc(2) # 2

在您的上下文中的示例

python load_function.py "def f(x): return x * 2"

//load_function.py
import sys

exec sys.argv[1]
print f(2)

命令行输出:
4

编辑:必须指出,“不明智地执行用户输入是不明智的。”


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接