Python中可以使用真正的动态和匿名函数吗?

Bil*_* VB 24 python

正如可以使用type(name,base-classes,namespace-dict)创建动态类一样,是否可以创建动态函数?

我尝试过以下方面的做法:

>>> f = type("f", (function,), {})
NameError: name 'function' is not defined
Run Code Online (Sandbox Code Playgroud)

好的,所以我会很聪明,但是:

>>> 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
Run Code Online (Sandbox Code Playgroud)

Python是否以与允许动态类相同的方式专门阻止动态函数的创建?

编辑:注意,我不允许任何使用exec ..因为我的问题是Python语言本身允许这样做.

提前致谢.

Anu*_*yal 37

还有types.FunctionType,你可以用它来动态地创建的功能,例如

def test_func(): print 'wow' 
dynf = types.FunctionType(test_func.func_code, {})
dynf()
Run Code Online (Sandbox Code Playgroud)

输出:

wow
Run Code Online (Sandbox Code Playgroud)

您可能会反对这不是动态的,因为我正在使用来自另一个函数的代码,但这只是一个示例,有一种方法可以从python字符串生成代码,例如

dynf = types.FunctionType(compile('print "really WoW"', 'dyn.py', 'exec'), {})
dynf()
Run Code Online (Sandbox Code Playgroud)

输出:

really WoW
Run Code Online (Sandbox Code Playgroud)

现在这是动态的!

OP担心这种功能的动态特性,所以这是另一个例子

dynf = types.FunctionType(compile('test_func():\ntest_func()', 'dyn.py', 'exec'), globals())
dynf()
Run Code Online (Sandbox Code Playgroud)

输出:

wow
wow
Run Code Online (Sandbox Code Playgroud)

注意:像这样创建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')
Run Code Online (Sandbox Code Playgroud)

输出:

WoW really WoW
Run Code Online (Sandbox Code Playgroud)

  • 请注意,似乎没有办法创建以这种方式接受任何参数的函数;编译的代码对象没有所需的 co_argcount。 (2认同)
  • @MartijnPieters 为通用函数添加了一个示例 (2认同)

Gar*_*tty 12

你需要研究一下collections.Callable,这只是定义时开始的好地方__call__.

from collections import Callable
class SomeCallableClass(Callable):
    def __call__(self, x):
        print(x)

some_function = SomeCallableClass()
some_function(1)
Run Code Online (Sandbox Code Playgroud)

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.")
Run Code Online (Sandbox Code Playgroud)

这给了我们:

Two times.
Two times.
Three times.
Three times.
Three times.
Run Code Online (Sandbox Code Playgroud)

您可以使用它来构建您想要的复杂函数.


jac*_*ev6 8

exec如果您准备生成抽象语法树(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()
Run Code Online (Sandbox Code Playgroud)

上面的代码将打印出来42.

要获得生成的AST应该是什么的灵感,您可以使用:

print(dump(parse("def f(): return 42"), include_attributes=True))
Run Code Online (Sandbox Code Playgroud)

当然,AST在Python 2和Python 3中有所不同.