如果已经提出并回答了这个问题,请道歉。 我需要做的是概念非常简单,但不幸的是我无法在网上找到答案。
我需要在运行时使用自定义名称在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
"""
提前致谢,
马赫迪
答案 0 :(得分:44)
对于你描述的内容,我认为你不需要进入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>
答案 1 :(得分:14)
延伸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>
答案 2 :(得分:6)
可能有一种反省做这种事情,但我不认为这是解决这个问题的pythonic方法。
我认为你应该利用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)
希望你的用例有意义。
答案 3 :(得分:2)
您可能想要使用eval;你将构建包含每个函数的Python定义的字符串(即以def func1
开头的多行字符串....)然后你将eval
它。
但我会为每个这样的函数生成一个唯一的名称(例如genfun345
)。请勿对此类名称使用某些未经检查的用户输入。因为如果名称与Python中的某个已知名称相同,那么您将遇到难以调试的灾难。
您是否相信这些功能的输入?您是否关心恶意软件或滥用?
在维基百科上阅读hygenic macros。
答案 4 :(得分: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)
答案 5 :(得分:0)
要真正真正地动态创建函数,可以使用makefun,我已经为此专门开发了它。它支持三种方法来定义要生成的签名:
'foo(a, b=1)'
Signature
对象,它是手工制作的或通过在另一个函数上使用inspect.signature
派生的此外,您可以告诉它将创建的函数的引用作为实现中的第一个参数注入,以便根据调用的来源(哪个外观)进行较小的行为修改。例如:
# generic core implementation
def generic_impl(f, *args, **kwargs):
print("This is generic impl called by %s" % f.__name__)
# here you could use f.__name__ in a if statement to determine what to do
if f.__name__ == "func1":
print("called from func1 !")
return args, kwargs
my_module = getmodule(generic_impl)
# generate 3 facade functions with various signatures
for f_name, f_params in [("func1", "b, *, a"),
("func2", "*args, **kwargs"),
("func3", "c, *, a, d=None")]:
# the signature to generate
f_sig = "%s(%s)" % (f_name, f_params)
# create the function dynamically
f = create_function(f_sig, generic_impl, inject_as_first_arg=True)
# assign the symbol somewhere (local context, module...)
setattr(my_module, f_name, f)
# grab each function and use it
func1 = getattr(my_module, 'func1')
assert func1(25, a=12) == ((), dict(b=25, a=12))
func2 = getattr(my_module, 'func2')
assert func2(25, a=12) == ((25,), dict(a=12))
func3 = getattr(my_module, 'func3')
assert func3(25, a=12) == ((), dict(c=25, a=12, d=None))
正如您在documentation中所看到的,参数总是重定向到kwargs
,除非绝对不可能(例如func2
中的可变位置签名)。 >