Python动态创建自定义命名函数

时间:2012-11-02 16:28:49

标签: python python-2.7 metaprogramming dynamic-function

  

可能重复:
  Python dynamic function creation with custom names

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

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

以下代码完全符合我的要求,但它不干净且非常暴力。我想弄清楚是否有更好的方法来做到这一点。

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循环打印args,它会做更有意义的事情。我从上面的代码中得到了我想要的确切结果,只是想知道是否有更好的方法。

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

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

提前致谢。

Mahdi

2 个答案:

答案 0 :(得分: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模块主要用于检查。

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

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

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

如果您对元编程感兴趣,那么选择的语言可能就是Common Lisp。编写编写代码的代码对于Lisp来说并不是特别的,而编写(编写代码的函数)当然比使用Lisp的常规运行时函数编写困难更困难必要的(问题确实比较困难)而且由于语言的限制而不是人为的。

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

答案 1 :(得分: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