正如可以使用type(name,base-classes,namespace-dict)创建动态类一样,是否可以创建动态函数?
我尝试过按照以下方式做的事情:
>>> f = type("f", (function,), {})
NameError: name 'function' is not defined
好的,所以我会很聪明,但是:
>>> def fn():
... pass
...
>>> type(fn)
<type 'function'>
>>> f = type("f", (type(fn),), {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type 'function' is not an acceptable base type
Python 具体是否会像动态类一样阻止动态函数的创建?
编辑:注意,我不允许使用任何exec ..因为我的问题是Python语言本身是否允许这样做。
提前致谢。
答案 0 :(得分:37)
可以使用types.FunctionType
动态创建一个函数,例如
def test_func(): print 'wow'
dynf = types.FunctionType(test_func.func_code, {})
dynf()
输出:
wow
你可能会反对这不是动态的,因为我正在使用来自另一个函数的代码,但这只是一个例子,有一种从python字符串生成代码的方法,例如。
dynf = types.FunctionType(compile('print "really WoW"', 'dyn.py', 'exec'), {})
dynf()
输出:
really WoW
现在这是动态的!
OP担心这种功能的动态特性所以这是另一个例子dynf = types.FunctionType(compile('test_func():\ntest_func()', 'dyn.py', 'exec'), globals())
dynf()
输出:
wow
wow
注意:
像这样创建Function对象似乎有限制,例如传递参数并不容易,因为要传递参数我们需要将正确的co_argcount,co_varnames和其他12个变量传递给types.CodeType
,这在理论上可以完成但是容易出错,更简单的方法是将字符串导入为一个模块,你有一个完整的功能,例如
import types
import sys,imp
code = """def f(a,b,c):
print a+b+c, "really WoW"
"""
module = imp.new_module('myfunctions')
exec code in module.__dict__
module.f('W', 'o', 'W')
输出:
WoW really WoW
答案 1 :(得分:12)
您需要查看collections.Callable
,这是定义__call__
时的好地方。
from collections import Callable
class SomeCallableClass(Callable):
def __call__(self, x):
print(x)
some_function = SomeCallableClass()
some_function(1)
会将1
作为输出。这允许您随意构建函数。
from collections import Callable
class SomeCallableClass(Callable):
def __init__(self, n):
self.n = n
def __call__(self, x):
for i in range(self.n):
print(x)
some_function = SomeCallableClass(2)
some_function("Two times.")
some_function = SomeCallableClass(3)
some_function("Three times.")
这给了我们:
Two times.
Two times.
Three times.
Three times.
Three times.
您可以根据需要使用它来构建复杂的函数。
答案 2 :(得分:8)
如果您已准备好生成Abstract Syntax Trees (AST's)并编译它们,则可以避免生成exec
源代码。它可能稍微好一点,因为数据可以一直保持结构化。
from ast import *
from types import *
function_ast = FunctionDef(
name='f',
args=arguments(args=[], vararg=None, kwarg=None, defaults=[]),
body=[Return(value=Num(n=42, lineno=1, col_offset=0), lineno=1, col_offset=0)],
decorator_list=[],
lineno=1,
col_offset=0
)
module_ast = Module(body=[function_ast])
module_code = compile(module_ast, "<not_a_file>", "exec")
function_code = [c for c in module_code.co_consts if isinstance(c, CodeType)][0]
f = FunctionType(function_code, {})
print f()
上面的代码将打印42
。
要获得生成的AST应该是什么的灵感,您可以使用:
print(dump(parse("def f(): return 42"), include_attributes=True))
当然,AST在Python 2和Python 3中有所不同。
答案 3 :(得分:4)
许多人似乎被误导了Python中“lambda”的目的:它的唯一目的是定义一个没有名称的简单单表达式函数。而已。在Python中,函数确实是第一类对象,就像它们在LISP中一样:您可以将它们作为参数传递,将它们存储在数据结构中,并将它们作为结果返回。例如,这是一个函数,它组成了两个给定的函数f和g,因此compose(f,g)(x)等价于f(g(x)):
def compose(f, g) :
def result(x) :
return f(g(x))
#end result
return result
#end compose
这是一个使用示例:
>>> h = compose(lambda x : x + 2, lambda x : x * x)
>>> h(3)
11
答案 4 :(得分:2)
是。使用def
声明:
def functor(f): # this dynamically creates a function at module load time
def inner(g): #this dynamically creates a function on execution of functor
return f(g)
return inner
任何涉及编译独立文本的解决方案都等同于exec
或eval
,这使您可以使用预先存在的函数和词汇捕获的数据项来拼接新函数。 This可能会给你一些想法。
答案 5 :(得分:1)
Python确实允许创建动态函数。一种方法是使用lambda:
>>> g = lambda x: x**2
>>> g
<function <lambda> at 0xa68c924>
>>> g(3)
9
>>> g = lambda x: x*2
>>> g
<function <lambda> at 0xa68c95c>
>>> g(3)
6
>>>
此处描述了另一种方法:Lexical closures in Python
所以,你不需要像Strategy那样的行为模式。
如果你能告诉我们你想要解决的问题,那将是有用的,这样我们就可以找到适合的语言结构。
答案 6 :(得分:0)
jacquev6的解决方案对我来说效果很好在Python 3上对其进行更新之后,方法是在对arguments()的调用中添加 kwonlyargs = [],kw_defaults = [] : / p>
#!/usr/bin/env python3
from ast import *
from types import *
function_ast = FunctionDef(
name='f',
args=arguments(args=[], vararg=None, kwarg=None, defaults=[], kwonlyargs=[], kw_defaults=[]),
body=[Return(value=Num(n=42, lineno=1, col_offset=0), lineno=1, col_offset=0)],
decorator_list=[],
lineno=1,
col_offset=0
)
module_ast = Module(body=[function_ast])
module_code = compile(module_ast, "<not_a_file>", "exec")
function_code = [c for c in module_code.co_consts if isinstance(c, CodeType)][0]
f = FunctionType(function_code, {})
print(f())