2012-04-24 4 views
21

type(name、base-classes、namespace-dict)を使用して動的クラスを作成できるように、動的関数を作成できますか?Pythonで真の動的関数と匿名関数が可能ですか?

私はの線に沿って何かやってみた:特にが動的の作成を防ぐ

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

>>> f = type("f", (function,), {}) 
NameError: name 'function' is not defined 

[OK]を、ので、私は賢いだろうが、動的クラスを可能にするのと同じ方法で機能しますか?

編集:私はexecの使用を許可しません。私の質問はPython言語自体が許可しているからです。

ありがとうございます。

+3

で異なっている: 'FOO

あなたが使用することができ、生成されたASTはどうあるべきかについてのインスピレーションを取得するには=λx:x * 2 'である。そして、あなたはどんなクラスも呼び出し可能にすることができます... –

+1

しかし、私はPythonのラムダは、無名関数としてのラムダの古典的な概念と同等であるとは思いませんか? Pythonのラムダは、基本的に単一の式しか扱えないので、はるかに制限されています。 –

+1

ですが、リストの理解は単一の式なので、ループ、割り当て、副作用、フローの制御、それらのネストを行うことができます。 – ch3ka

答えて

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 

注:これは、例えば限界を持っているように見えるよう は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 
+0

ええ、ありがとう。私の質問は少し不十分であると思う。 dynfがtest_funcを2回呼び出すようにしたいのですが? –

+0

@ B.VB。私はあなたに本当のダイナミックな機能を尋ねましたが、それはあなたが通常の機能として何かをして、何らかの変更をしようとします。 –

+0

この方法で引数を受け入れる関数を作成する方法がないように見えることに注意してください。コンパイルされたコードオブジェクトには、必要なco_argcountがありません。 –

10

あなたは__call__を定義するときに起動するだけで良い場所である、collections.Callableに見たいと思うでしょう。

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

これは興味深いです。これは、より一般的な関数の構築方法として構築することができるようです。私はもう少しこれについて考える必要があります。ありがとう。 –

+0

関数を動的に作成するために、独自の呼び出し可能なクラスを定義する必要はありません。実際には多くの点でそれはあまり動的ではありません。 – Marcin

+0

@Marcinもちろん、必要はありませんが、場合によってはそれをモデル化するのにいい方法です。他のオープンは他の多くの答えです。 –

1

Pythonでは動的関数を作成できます。 Lexical closures in Python

だから、あなたはStrategyのような行動パターンのホーカス・ポーカス-を必要としません。別のアプローチは、ここで説明されて

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

:一つのアプローチは、ラムダを使用しています。

あなたが解決したい問題を教えて、どの言語構成が適切かどうかを知ることができれば役に立ちます。

3

多くの人がPythonの "lambda"の目的について誤解されているようです。その唯一の目的は、名前のない単純な単一式関数を定義することです。これ以上何もない。 Pythonでは、関数は実際にはLISPのようなファーストクラスのオブジェクトです。引数として渡したり、データ構造に格納したり、結果として返すことができます。例えば、ここでfおよびg 2つの所与の機能を構成する関数であるので、(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 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に相当し、既存の機能と辞書的にキャプチャされたデータ項目が残ります:defステートメントを使用します。 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が印刷されます。

もちろん
print(dump(parse("def f(): return 42"), include_attributes=True)) 

、ASTのは、あなたがラムダ関数を持つことができるのPython 2とPython 3

+0

あなたのコードを実行中、私はこのエラーがあります: 'TypeError:必須フィールド" kwonlyargs "が引数から抜けています –

+0

私はあなたの答えを編集します –

+0

回答を編集中です! –

関連する問題