我写了一个小脚本来确定我想做的事情是否可行。它是。
我的目标是动态地(在运行时)创建具有基于任意大小列表的名称的函数(或方法)(列表的大小=动态创建的函数的数量)。 所有函数都是相同的(现在),它们只是打印它们的参数。
以下代码完全符合我的要求,但它不干净且非常暴力。我想弄清楚是否有更好的方法来做到这一点。
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
答案 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