1

可能重复:
使用自定义名称创建 Python 动态函数

我写了一个小脚本来确定我想做的事情是否可行。这是。

我的目标是动态地(在运行时)创建函数(或方法),其名称基于任意大小的列表(列表大小 = 动态创建的函数数)。所有的函数都做同样的事情(现在),它们只是打印它们的参数。

下面的代码正是我想要的,但是,它不干净而且非常蛮力。我试图弄清楚是否有更好的方法来做到这一点。

class Binder:
    def __init__(self, test_cases):
        """"
        test_cases: a list of function/method names.
        length of test_case = number of methods created.
        """

        for test_case in test_cases:
            #construct a code string for creating a new method using "exec" 
            func_str = "def "
            func_str += test_case
            func_str += "(*args):"
            func_str += "\n\t"
            func_str += "for arg in args:"
            func_str += "\n\t\t"
            func_str += "print arg"
            func_str += "\n"

            """
            For example, func_str for test_cases[0]= "func1" is simply:
            def func1(*args):
                for arg in args:
                    print arg
            """
            #use exec to define the function
            exec(func_str)

            #add the function as a method to this class
            # for test_cases[0] = "func1", this is: self.func1 = func1
            set_self = "self." + test_case + " = " + test_case
            exec(set_self)

if __name__ == '__main__':
    #this list holds the names of the new functions to be created
    test_cases = ["func1", "func2", "func3", "func4"]
    b = Binder(test_cases)

    #simply call each function as the instant's attributes
    b.func1(1)
    b.func2(1, 3, 5)
    b.func4(10)

输出是:

1
1
3
5
10

正如预期的那样。

更新函数的内容不仅仅是一个打印参数的for循环,它会做一些更有意义的事情。我从上面的代码中得到了我想要的确切结果,只是想知道是否有更好的方法。

更新我正在绑定一个更大模块的两端。一端确定测试用例是什么,除此之外,填充测试用例名称的列表。另一端是函数本身,它必须与测试用例名称进行 1:1 映射。所以我有了测试用例的名称,我知道我想对每个测试用例做什么,我只需要创建具有测试用例名称的函数。由于测试用例的名称是在运行时确定的,因此基于这些测试用例的函数创建也必须在运行时进行。测试用例的数量也是在运行时确定的。

有一个更好的方法吗??欢迎任何和所有建议。

提前致谢。

马赫迪

4

2 回答 2

1

在 Python 中,这是通用元编程最合理的方法。

如果您只需要代码中的一些常量,那么闭包可能会解决问题......例如:

def multiplier(k):
    "Returns a function that multiplies the argument by k"
    def f(x):
        return x*k
    return f

对于任意代码生成,Python 有一个ast模块,理论上您可以使用该方法检查或创建函数。然而,在 Python 代码中很难以这种方式表示和操作,因此通常的方法是在运行时完成所有操作,而不是编译特定的函数。当您真的可以获得优势时,您可以使用eval(获得 lambda)或exec. ast 模块主要用于检查。

编写生成代码的代码就是所谓的元编程,Python 对这种方法不是很友好。

您可能听说过 C++ 声称支持元编程,但实际上它只是基于模板的元编程,您可以在其中将类型常量替换为固定结构您可以使用像SFINAE规则这样的“高级元编程”来玩一些技巧,但它们只不过是诡计。

Python 不需要模板,因为该语言不是静态类型的并且您有闭包(C++ 是静态类型的并且没有闭包),但是 Python 对通用元编程方法没有帮助(即编写生成或操作代码的通用代码)。

如果您对元编程感兴趣,那么选择的语言可能是 Common Lisp。编写代码对 Lisp 来说并不是什么特别的事情,虽然编写(编写代码的函数)当然比仍然使用 Lisp 的常规运行时函数更难,但困难大多是必不可少的(问题确实更难) 而不是因为语言限制而人为的。

lispers 之间有一个老笑话,或多或少类似于“我正在编写代码,编写代码,编写代码,编写代码,人们付钱给我”。元编程确实只是第一步,之后你就有了元元编程(编写代码来编写代码生成器)等等。当然,事情变得越来越难(但再次因为问题更难,而不是因为任意强加的限制......)。

于 2012-11-02T17:07:04.923 回答
0

首先,这可能是一个非常糟糕的主意。要了解原因,请阅读评论和其他答案。

但是要回答您的问题,这应该可行(尽管它是一种技巧并且可能会产生奇怪的副作用):

from types import MethodType

def myfunc(self, *args):
    for arg in args:
        print arg

class Binder(object):
    def __init__(self, test_cases):
        for test_case in test_cases:
            method = MethodType(myfunc, self, Binder)
            setattr(self, test_case, method)

在这种情况下,对函数的引用是硬编码的,但您当然也可以将其作为参数传递。

这是输出:

>>> b = Binder(['a', 'b'])
>>> b.a(1, 2, 3)
1
2
3
>>> b.b('a', 20, None)
a
20
None
于 2012-11-02T17:18:31.747 回答