1

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 等基本语句。

4

3 回答 3

4

There are a number of ways to do this kind of thing.

If the function can be described without "stepping outside the language", you can just define a local function and return it, as in Blender's answer. This is usually what you want when you think you need to define new functions (borrowing Blender's example):

def make_func(a, b):
    def f(n):
        return n**a + b
    return f

Sometimes, you can do even better, and represent the functions as data. For example, how do you create an arbitrary polynomial function? Well, you don't have to; you can have a generic polynomial function that takes a list of coefficients and a value and evaluates it; then all you need to do is create coefficient lists.

In fact, I think this is the one you want here. As you say:

It may be return 2*f(n-1) - 4*f(n-2) - 5*f(n-3) + 15*f(n-4) one minute, or return f(n-1) + 3*f(n-2) another, or f(n-1)+f(n-2)+f(n-3)+f(n-4)+5*f(n-5) depending on what I need it to be.

This can definitely be represented as a list of coefficients:

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

But it's probably even simpler to write a single eval_recursive_func(coefficients, baseval), if all you're ever going to do with the returned function is call it immediately and then forget it.

Sometimes—rarely, but not never—you really do need to execute code on the fly. As Himanshu says, eval and exec and friends are the way to do this. For example:

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)

Now the f function has been defined, exactly as if you'd just done this:

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)

It's a bit different in Py3 than Py2, and there are variations depending on what context you want things executed in, or whether you just want it executed or evaluated or compiled or treated like an import, etc. But this is the basic idea.

If you can't think of why you'd want to write the first instead of the second, then you don't need this.

And if you can't figure out how to generate the right string on the fly, you shouldn't be doing this.

And, as Ignacio Vazquez-Abrams points out, if these functions can be built out of user input, you need to do something to validate that they're safe, usually by compiling iteratively and walking the AST.

Finally, even more rarely, you need to use the new module (and/or inspect) to create a new function object on the fly out of bits of other function objects (or even out of hand-crafted bytecode). But if you need to know how to do that, you probably already know how.

于 2012-12-10T04:44:29.360 回答
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)])

输出与硬编码函数的输出相匹配。

于 2012-12-10T04:36:33.523 回答
2

这很有意义。Python 甚至为此目的明确地提供了一组模块。不过,请确保在执行函数之前遍历 AST 并验证节点,以确保没有人潜入os.system('rm -rf /')其中。

于 2012-12-10T04:38:42.310 回答