【问题标题】:How do I pass function definition to python script as string如何将函数定义作为字符串传递给 python 脚本
【发布时间】:2016-04-21 03:05:54
【问题描述】:

我想将 函数定义 传递给 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. 清理

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

【问题讨论】:

  • 我认为您应该解释为什么要将函数作为字符串传递给运行的原因。这可以帮助我们更好地为您提供帮助,并可能找到一种解决方法来解决必须使用当前答案所建议的内容(eval 和 exec)。评估命令行参数并将其作为代码运行是极其危险的,除非绝对必要,否则不应这样做。
  • 只是出于好奇 - 为什么需要这个?我问是因为它会导致非常糟糕的解决方案(评估),也许您只是看不到其他可能性..
  • 我正在编写一个 shell 应用程序,用于对其他应用程序的日志/输出进行 map/reduce/filter 操作

标签: python function parsing command-line-arguments


【解决方案1】:

您可以使用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)

【讨论】:

  • 我强烈建议使最后一句话(''理想情况下,永远不必使用 eval。'')更明显(粗体)或改写它,例如:“从不,永远,永远不要使用eval,除非您绝对确定没有其他办法。即便如此,您也应该停下来再想一想。”
  • eval 背后的危险是什么?
  • @JanOsch - 有人可以按字面意思传递 anything 并且它会被执行。例如,一些小丑可以通过:“def f(x): import shutil; shutil.rmtree('/');”并删除您的文件系统。 Eval 是您程序中最大最危险的后门。
【解决方案2】:

您可以使用 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

编辑:必填,“这样执行用户输入是不明智的。”

【讨论】:

    【解决方案3】:

    使用函数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]
    

    【讨论】:

      【解决方案4】:

      有关如何执行此操作的正确答案的最明显来源是 timeit python builtin 库。

      它是这样调用的:

      $ python -m timeit '"-".join(str(n) for n in range(100))'
      

      你可以在这里找到源代码,它使用compileexec从命令行调用代码

      https://hg.python.org/cpython/file/2.7/Lib/timeit.py#l143

      【讨论】:

        猜你喜欢
        • 2022-12-09
        • 1970-01-01
        • 1970-01-01
        • 2016-01-08
        • 2016-09-15
        • 2015-06-07
        • 2018-06-25
        • 2020-10-11
        • 1970-01-01
        相关资源
        最近更新 更多