从字符串创建函数对象

12

问题:在Python中,有没有一种使用字符串来创建函数对象的方法?


信息:我正在开发一个项目,将数据存储在sqlite3服务器后端上。这并不是什么特别的事情。由于生成的代码非常枯燥乏味,因此DAL类通常通过代码生成来完成。但这给了我一个想法。在Python中,如果找不到属性,如果定义了函数__getattr__,则会在出现错误之前先调用它。所以我的想法是,通过解析器和逻辑树,在首次调用时动态生成所需的代码,然后将函数对象保存为本地属性。例如:

DAL.getAll()

#getAll() not found, call __getattr__

DAL.__getattr__(self,attrib)#in this case attrib = getAll

##parser logic magic takes place here and I end up with a string for a new function

##convert string to function

DAL.getAll = newFunc

return newFunc
我已经尝试了编译函数,但exec和eval在能够完成这种任务方面远非令人满意。我需要一些可以允许多行函数的东西。除了那些涉及将它写入磁盘的方法外,是否还有其他方法可以实现此目的?再次强调,我正在尝试动态创建函数对象
P.S.:是的,我知道这会带来严重的安全和稳定性问题。是的,我知道这是一个可怕的低效率的做法。但我不在乎。这是一个概念验证。我想知道“Python能否做到这一点?它能动态创建函数对象吗?”而不是一些更好的替代方案。(当然,在回答问题后,请自由附上更好的替代方案)

"exec和eval在能够完成这种任务方面远非令人满意"? 什么?exec完全可以做到。你遇到了什么具体的错误或问题? - S.Lott
1
尝试从exec返回一些内容。 - Narcolapser
exec 只是运行代码,它们按照字面意思执行。a = eval("123") 将 123 绑定到 a 上。你会得到表达式的值。 - Skurmedel
@Narcolapser:那么你可能需要类似于codeop库或py_compile的东西。例如:exec(codeop.compile_command("import os; print(os.getcwd())")) - Skurmedel
2
@Narcolapser:exec 函数没有返回任何东西,它只是用来创建函数。在运行 exec 之后,该函数就被创建出来了,你可以直接使用它。 - S.Lott
显示剩余3条评论
3个回答

27
以下代码将您在字典d中定义的符号放入字符串中:
d = {}
exec "def f(x): return x" in d

现在d ['f']是一个函数对象。如果您想在字符串中的代码中使用程序中的变量,则可以通过d发送此变量:

d = {'a':7}
exec "def f(x): return x + a" in d

现在,d['f']是一个函数对象,它被动态地绑定到d['a']。当你改变d['a']时,你改变了d['f']()的输出。


2
正如S.Lott所说,如果您只运行exec而不加“in”,则会在当前命名空间中创建一个新函数。执行“def f(x): return x”将给您一个新的函数对象f,可以在您的代码中使用。 - Gurgeh
这正是我想要的。我想我缺少的是没有考虑在我的exec语句中加入'def'。但这正是我想要的。谢谢。 - Narcolapser
1
即使使用 "from math import *",如果将 x 替换为 sin(x),这个方法也不起作用。看来它只适用于像 x + 1.0 这样的简单数学运算。 - Taozi
1
@Taozi,它运行得很好。你的作用域不会神奇地转移到d,你必须添加你想要使用的名称,像这样:d = {'a':7, 'sin':math.sin},如果你之前导入了math,它将起作用。 - Gurgeh
4
请注意,在Python 3+中,您需要使用exec(string, d)(它是一个函数,就像现在的print一样);请参阅此SO问题,了解Py2与Py3之间差异的详细信息:https://dev59.com/6WUp5IYBdhLWcg3wvZVV。 - Nathan
可以通过在一个方法中调用 exec("""def foo(x):\n print(x)""", self.__dict__) 来向类添加函数,例如在 init() 方法中。 - Thomas

2

你不能做像这样的事情吗?

>>> def func_builder(name):
...  def f():
...   # multiline code here, using name, and using the logic you have
...   return name
...  return f
... 
>>> func_builder("ciao")()
'ciao'

基本上,需要组合一个真实的函数而不是组合一个字符串,然后试图将其编译成一个函数。

0
如果只是概念证明,那么 eval 和 exec 是可以的,您还可以使用 pickle 字符串、yaml 字符串或任何其他您决定编写构造函数的内容来实现这一点。

1
尝试从exec返回一些内容。 - Narcolapser
这是正确的方法。使用现有的解析器来解析您的字符串,然后让它填充函数对象中的变量。如果它是完整的函数,则需要获取更强大的解析器。 - wheaties
就像 PIL 的 'tostring' 和 'fromstring' 方法一样,喜欢 yamls 对象创建似乎也很有趣。 - Jakob Bowyer

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