31

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言語自体がこれを許可するかどうかです。

前もって感謝します。

4

8 に答える 8

47

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
于 2012-04-24T18:07:20.787 に答える
14

を調べたいと思うでしょうcollections.Callable。これは、 を定義するときに開始するのに適した場所__call__です。

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

some_function = SomeCallableClass()
some_function(1)

1out 出力として表示されます。これにより、自由に関数を構築できます。

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.

これを使用して、必要なだけ複雑な関数を構築できます。

于 2012-04-24T18:07:58.147 に答える
12

抽象構文木 (AST)を生成して代わりにコンパイルするexec準備ができている場合は、ソース コードを生成してから ing することを避けることができます。データはずっと構造化されたままでいられるので、少し良いかもしれません。

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))

もちろん、Python 2 と Python 3 では AST が異なります。

編集:

Python 3.8 でテスト済みおよび動作中

from ast import *
from types import *

function_ast = FunctionDef(
    name='f',
    args=arguments(
        args=[], vararg=None, kwarg=None, defaults=[],
        kwonlyargs=[], kw_defaults=[], posonlyargs=[]
    ),
    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], type_ignores=[])

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())
于 2015-04-28T18:45:36.073 に答える
4

多くの人は、Python の「ラムダ」の目的について誤解しているようです。その唯一の目的は、名前のない単純な単一式関数を定義することです。これ以上何もない。Python では、関数は、たとえば LISP の場合と同様に、実際にファーストクラスのオブジェクトです。関数を引数として渡し、データ構造に格納し、結果として返すことができます。たとえば、次の関数は与えられた 2 つの関数 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
于 2012-04-25T02:13:52.797 に答える
2

Python では、動的関数を作成できます。1 つのアプローチは、ラムダを使用することです。

>>> 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
>>> 

別のアプローチがここで説明されています: Python のレキシカル クロージャ

したがって、 Strategyのような行動パターンの焦点は必要ありません。

解決したい問題を教えていただければ、それに適した言語構造を見つけることができます。

于 2012-04-24T18:16:33.133 に答える