2012-11-01 91 views
27

如果已经提出并回答了此问题,请道歉。 我需要做的是非常简单的概念,但不幸的是,我一直无法在网上找到答案。使用自定义名称创建Python动态函数

我需要在运行时用自定义名称在Python(Python2.7)中创建动态函数。每个函数的主体也需要在运行时构造,但对于所有函数来说(几乎)是相同的。

我从名字列表开始。

func_names = ["func1", "func2", "func3"] 

注意,FUNC_NAME列表可以保存任意名称的列表,所以名称将不仅仅是FUNC1,FUNC2,FUNC3,....

我想要的结果是:

def func1(*args): 
     ... 

    def func2(*args): 
     ... 

    def func3(*args): 
     ... 

我需要这样做的原因是,每个函数名称都对应于一个测试用例,然后从外部调用该测试用例。

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

更新:我也可以将这个自定义命名函数包装在类中,如果这样会让事情变得更容易。

我可以硬编码函数的内容(因为它们几乎是相同的)在一个字符串中,或​​者我可以将它从之前定义的基类中取出。只需要知道如何用这个内容填充函数。

例如:

func_content = """ 
        for arg in args: 
         print arg 
        """ 

由于提前,

马赫迪

回答

1

您可能需要使用eval;你将构建包含每个函数的Python定义的字符串(即以def func1 ....开头的多行字符串),然后你会得到它eval它。

但我会为每个这样的函数生成一个唯一的名称(例如genfun345)。不要对这些名称使用一些未经检查的用户输入。因为如果名称与Python中的某个已知名称相同,则很难调试出灾难。

你相信这些功能的输入吗?你关心恶意软件还是滥用?

阅读关于hygenic macros上的维基百科。

+0

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

33

对于你所描述的,我不认为你需要下降到eval或宏 - 通过闭包创建函数实例应该工作得很好。例如:

def bindFunction1(name): 
    def func1(*args): 
     for arg in args: 
      print arg 
     return 42 # ... 
    func1.__name__ = name 
    return func1 

def bindFunction2(name): 
    def func2(*args): 
     for arg in args: 
      print arg 
     return 2142 # ... 
    func2.__name__ = name 
    return func2 

但是,您可能希望将这些函数按名称添加到某个范围,以便您可以按名称访问它们。

>>> print bindFunction1('neat') 
<function neat at 0x00000000629099E8> 
>>> print bindFunction2('keen') 
<function keen at 0x0000000072C93DD8> 
+0

简单而有效。 –

6

有可能是一种内省做这种事情的,但我不认为这将是Python的解决问题的方法。

我认为你应该利用python中的函数性质作为一级公民。按照Shane Holloway指出的那样使用闭包,根据需要自定义功能内容。然后对于动态名称绑定,使用一个字典,其中的键是动态定义的名称,并且这些值将是函数本身。

def function_builder(args): 
    def function(more_args): 
     #do stuff based on the values of args 
    return function 

my_dynamic_functions = {} 
my_dynamic_functions[dynamic_name] = function_builder(some_dynamic_args) 

#then use it somewhere else 
my_dynamic_functions[dynamic_name](the_args) 

希望它对您的使用情况有意义。

1

如果我正确理解你的要求,这听起来像你只是想动态地分配现有功能的新的或替代的名字 - 在这种情况下的东西沿着以下行应该做的工作:

import sys 

testcases = [] 
def testcase(f): 
    """ testcase function decorator """ 
    testcases.append(f) 
    return f 

@testcase 
def testcase0(*args): 
    print 'testcase0 called, args:', args 

@testcase 
def testcase1(*args): 
    print 'testcase1 called, args:', args 

@testcase 
def testcase2(*args): 
    print 'testcase2 called, args:', args 

def assign_function_names(func_names, namespace=None): 
    if namespace is None: 
     namespace = sys._getframe(1).f_globals # default to caller's globals 
    for name, func in zip(func_names, testcases): 
     func.__name__ = name # optional 
     namespace[name] = func 

assign_function_names(["funcA", "funcB", "funcC"]) 

funcA(1, 2, 3) 
funcB(4, 5) 
funcC(42) 
+0

感谢您的回复。但事实并非如此。它们不是现有的函数,此外,要动态创建的函数的数量也是未知的(仅在运行时才知道)。 – mahdiolfat

+0

如果你可以“硬编码函数的内容”,那么你可以在.py文件中使用'def xxx(yyy):'作为内容前的内容,并将其作为一个存在的函数 - 你认为你“通过将其放入字符串并动态创建一个函数来获得它? – martineau

+0

我想你已经误解了我在这里要做的事情,并没有真正回答我的问题,而是继续告诉我做别的事情。尽管如此。我已经解决了我的问题。 – mahdiolfat

10

扩展到Shane的答案,因为在寻找类似问题的解决方案时我刚发现这个问题。注意变量的范围。您可以通过使用生成器函数来定义范围来避免范围问题。这里是一个定义一个类方法的一个例子:

class A(object): 
    pass 

def make_method(name): 
    def _method(self): 
     print("method {0} in {1}".format(name, self)) 
    return _method 

for name in ('one', 'two', 'three'): 
    _method = make_method(name) 
    setattr(A, name, _method) 

在使用中:

In [4]: o = A() 

In [5]: o.one() 
method one in <__main__.A object at 0x1c0ac90> 

In [6]: o1 = A() 

In [7]: o1.one() 
method one in <__main__.A object at 0x1c0ad10> 

In [8]: o.two() 
method two in <__main__.A object at 0x1c0ac90> 

In [9]: o1.two() 
method two in <__main__.A object at 0x1c0ad10>