2012-04-24 25 views
21

正如动态类可以使用类型(名称,基类,命名空间字典)创建,可以创建一个动态函数?在Python中可以使用真正的动态和匿名函数吗?

我试着做线沿线的东西:

>>> 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语言本身是否允许这样做。

在此先感谢。

+3

不同您可以lambda函数:'FOO =λx:x * 2'。你可以让任何类可以调用,所以... – 2012-04-24 17:55:28

+1

但我不认为Python中的lambda等价于lambda作为匿名函数的经典概念,对吧? python中的lambda远远更有限,因为它基本上只能处理单个表达式,对吗? – 2012-04-24 17:58:27

+1

但由于列表理解是一个单一的表达式......你可以循环,分配,有副作用,控制流,嵌套这些... – ch3ka 2012-04-24 18:00:40

回答

30

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 

注: 创建这样的功能对象似乎有限制如传递参数并不容易,因为为了传递参数,我们需要将正确的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 
+0

恩..谢谢。我认为我的问题有点不明确。如果我现在想要dynf调用test_func两次? – 2012-04-24 18:11:44

+0

@ B.VB。我向你展示了你问了一个真正的动态函数,它会做你想做的任何事情作为一个正常的函数,尝试做一些改变。 – 2012-04-24 18:19:11

+0

请注意,似乎没有办法以这种方式创建接受任何参数的函数;编译的代码对象没有所需的co_argcount。 – 2012-07-02 11:39:38

10

你会想看看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. 

你可以用这个来构建功能复杂,只要你想。

+0

这越来越有趣。看起来,这可以构建成更一般的构造函数的方式。我需要更多地考虑这一点。谢谢。 – 2012-04-24 18:24:28

+0

没有必要定义一个自己的可调用类来动态创建函数;事实上在很多方面都不那么活跃。 – Marcin 2014-01-31 20:51:24

+0

@Marcin当然你不需要*,但在某些情况下,这是一个很好的建模方法。另一个打开的是许多其他的答案。 – 2014-02-01 21:47:03

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的戏法。

如果你能告诉我们你想解决的问题,那么我们可以找出哪种语言结构适合于这个问题,这将是有用的。

3

很多人似乎误解了Python中“lambda”的用途:它的唯一目的是定义一个没有名称的简单单表达式函数。而已。在Python中,函数确实是一类对象,就像它们在LISP中那样:您可以将它们作为参数传递,将它们存储在数据结构中,然后将它们作为结果返回。例如,这里是构成两个给定的函数f和g,因此构成一个函数(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 
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 

任何涉及独立式文本被编译的解决方案是相当于execeval,这让你预先存在的功能和词汇捕获的数据项拼接在一起的新功能。 This可能会给你一些想法。

3

如果您准备好生成AST并对其进行编译,则可以避免生成源代码。这可能会稍微好一点,因为数据可以始终保持结构。

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

+0

执行你的代码,我有这个错误: 'TypeError:必填字段“kwonlyargs”从参数中丢失 – 2017-11-04 07:46:17

+0

我将编辑你的答案 – 2017-11-04 07:52:25

+0

编辑! – 2017-11-04 07:53:31

相关问题