Python中有没有办法在Python脚本中创建Python代码然后执行/测试呢?
我的功能有以下类型的表格(例如)
def f(n):
if n<=3: return [0, 0, 6, 12][n]
return 2*f(n-1) - 4*f(n-2) - 5*f(n-3) + 15*f(n-4)
但是我希望能够动态地创建这些类型的函数(或任何任意函数),然后在运行时测试它们的输出(而不是将此函数复制/粘贴到脚本中,然后手动测试它) 。
不确定这是否有意义,请在需要时请求详细说明。我已经研究过eval和exec,但是无法让它们使用整个函数定义,只需要像1 + 2这样的基本语句等。
答案 0 :(得分:4)
有很多方法可以做这种事情。
如果可以在没有“走出语言”的情况下描述该功能,您可以定义一个本地函数并将其返回,如Blender的答案。当您认为需要定义新函数时,这通常是您想要的(借用Blender的示例):
def make_func(a, b):
def f(n):
return n**a + b
return f
有时,您可以做得更好,并将功能表示为数据。例如,如何创建任意多项式函数?好吧,你不必;你可以有一个泛型多项式函数,它接受一系列系数和一个值并对其进行评估;那么你需要做的就是创建系数列表。
事实上,我认为这是你想要的。正如你所说:
可以返回2 * f(n-1) - 4 * f(n-2) - 5 * f(n-3)+ 15 * f(n-4)一分钟,或返回f(n -1)+ 3 * f(n-2)另一个,或f(n-1)+ f(n-2)+ f(n-3)+ f(n-4)+ 5 * f(n-5) )取决于我需要它。
这绝对可以表示为系数列表:
def make_recursive_func(coefficients, baseval):
def f(n):
if n < len(coefficients): return baseval[n]
return sum(coefficient * f(n-i-1) for i, coefficient in enumerate(coefficients))
return f
但是编写一个eval_recursive_func(coefficients, baseval)
可能更简单,如果您对返回的函数所做的只是立即调用它然后忘记它。
有时 - 很少,但不是永远 - 你真的需要动态执行代码。正如Himanshu所说,eval
和exec
和朋友是这样做的方式。例如:
newcode = '''
def f(n):
if n<=3: return [0, 0, 6, 12][n]
return 2*f(n-1) - 4*f(n-2) - 5*f(n-3) + 15*f(n-4)
'''
exec(newcode)
现在已经定义了f
函数,就像你刚刚这样做了一样:
def f(n):
if n<=3: return [0, 0, 6, 12][n]
return 2*f(n-1) - 4*f(n-2) - 5*f(n-3) + 15*f(n-4)
在Py3中它与Py2有点不同,并且根据你想要执行的内容有什么变化,或者你只是想要它执行,评估,编译或者像导入一样处理等等。但这是基本的想法。
如果你想不出为什么要写第一个而不是第二个,那么你就不需要了。
如果你无法弄清楚如何动态生成正确的字符串,你就不应该这样做。
而且,正如Ignacio Vazquez-Abrams指出的那样,如果这些功能可以建立在用户输入之外,那么你需要做一些事情来验证它们是否安全,通常是通过迭代编译和走AST来实现。
最后,更少见的是,你需要使用new
模块(和/或inspect
)来动态创建一个新的函数对象(或者甚至是手工制作的字节码)。但是如果你需要知道如何做到这一点,你可能已经知道如何做了。
答案 1 :(得分:3)
如果您的功能相似,您可以使用其他功能创建它们:
def make_func(a, b):
def f(n):
return n**a + b
return f
使用make_func
转换此函数定义:
def g(n):
return n**2 + 1
进入这个:
g = make_func(2, 1)
在你的情况下,这样的事情应该有效:
def create_f(start_condition, vars, coeff_pairs):
def x(n):
if n <= start_condition:
return vars[n]
result = 0.0
for coeff, shift in coeff_pairs:
result += coeff * x(n + shift)
return result
return x
你可以用:
来调用它f = create_f(3, [0, 0, 6, 12], [(2, -1), (-4, -2), (-5, -3), (15, -4)])
输出与硬编码功能的输出相匹配。
答案 2 :(得分:2)
这很有道理。 Python甚至为此目的明确地有一个set of modules。确保在执行函数之前先走AST并验证节点,以确保有人没有偷偷进入os.system('rm -rf /')
。