2009-08-05 22 views
5

関数Pythonで与えられた関数のネストされた(ローカル)関数を調べる

def f(): 
    x, y = 1, 2 
    def get(): 
     print 'get' 
    def post(): 
     print 'post' 

には、ローカルのget()関数とpost()関数に私がそれらを呼び出すことができる方法でアクセスする方法がありますか?私は上で定義された関数f()のように動作する関数を探しています。

>>> get, post = get_local_functions(f) 
>>> get() 
'get' 

import inspect 
for c in f.func_code.co_consts: 
    if inspect.iscode(c): 
     print c.co_name, c 

のようなローカル関数のコードオブジェクトにアクセスできます

get <code object get at 0x26e78 ...> 
post <code object post at 0x269f8 ...> 

実際の呼び出し可能な関数オブジェクトを取得する方法を理解できません。それも可能ですか?

助けてくれてありがとう、

ウィル。

+0

私は私の実際のユースケースのために、私はそうのようなネストされた/ローカル関数を呼び出すことができるようにする必要があることに注意する必要があります。 GET(要求、* argsを、** kwargsからの) –

答えて

4

あなたはそれを行うのはかなり接近している - ちょうどnewモジュール行方不明:

import inspect 
import new 

def f(): 
    x, y = 1, 2 
    def get(): 
     print 'get' 
    def post(): 
     print 'post' 

for c in f.func_code.co_consts: 
    if inspect.iscode(c): 
     f = new.function(c, globals()) 
     print f # Here you have your function :]. 

をしかし、なぜheckがわざわざ?クラスを使用する方が簡単ですか?インスタンス化はとにかく関数呼び出しのように見えます。

+0

ネストされた関数がクロージャでない限り、それはトリックを行います。ありがとう! あなたとAnts Aasmaは正しいですが、私はちょうどこれのためにクラスを使用する必要があります。私がしていないのは、カスタム認証などですでに多くのビューデコレータが使用されているDjangoプロジェクトであり、これらのデコレータをすべてメソッドで動作させることを望まないからです。だから私は、RESTfulなビュー(http://github.com/jpwatts/django-restviews/tree/masterのような)へのクラスベースのアプローチをシミュレートしています。 –

+0

これは、あなたが現在のモジュールにfを持っているためにしかうまくいかないと思います。 – Quant

2

あなただけのPythonで他のオブジェクトと同様の機能を返すことができます。

def f(): 
    x, y = 1, 2 
    def get(): 
     print 'get' 
    def post(): 
     print 'post' 
    return (get, post) 


get, post = f() 

は、この情報がお役に立てば幸い!

ただし、get()またはpost()で 'x'および 'y'変数を使用する場合は、それらをリストにする必要があります。あなたはこのような何か行うと

は:

def f(): 
    x = [1] 
    def get(): 
     print 'get', x[0] 
     x[0] -= 1 
    def post(): 
     print 'post', x[0] 
     x[0] += 1 
    return (get, post) 

get1, post1 = f() 
get2, post2 = f() 

get1とPOST1をget2とPOST2とは異なる 'x' のリストを参照します。

+0

ええ、それは私は何を実際にです現時点でやっている。可能であれば、関数を明示的に返すことを避ける方法を見つけることを望んでいました。 –

+3

Pythonの** Zenを引用することになります**:* "明示的なものは暗黙的なものよりも優れています" * - と - * "特殊なケースはルールを破るのに特別なものではありません" * –

+0

良い点、Evan 。私が今持っているソリューションは、タプルの代わりにdictを返す必要があることを除いて、本質的にこれと同じですが、うまく動作します。私はちょうど返信dict(get = get、post = post、put = putなど)を繰り返し、Djangoのビューの一番下に置かないようにしたいと考えていました。しかし、アドバイスをいただきありがとうございます。 –

2

execを使用してコードオブジェクトを実行することができます。たとえば、あなたがfした場合の出力として

get 

を与えるだろう、その後

exec(f.func_code.co_consts[3]) 

、上記のように定義されました。

+0

ああ、そういうわけでコードオブジェクトを実行するのです!残念ながら、私の実際のユースケースでは、引数をローカル関数に渡す必要があります。 exec()を呼び出すときに私がそれを行う方法はありますか? –

1

関数f()が実行される前に、内部関数オブジェクトは存在しません。あなたがそれらを取得したい場合は、あなた自身でそれを構築する必要があります。関数のスコープから変数を取り込み、おそらくインタプリタの実装の詳細と見なされるべきオブジェクトを掘り下げることを必要とするクロージャーである可能性があるため、これは間違いではありません。

あなたは以下の繰り返しで関数を収集したい場合は、私は、次のいずれかの方法をお勧めします。

A)だけでクラス定義の関数を入れて、そのクラスへの参照を返します。名前でアクセスされる関連する関数のコレクションは、クラスのように非常に匂いがします。

b)関数を登録してデコレータとして使用するdictサブクラスを作成します。

class FunctionCollector(dict): 
    def register(self, func): 
     self[func.__name__] = func 

def f(): 
    funcs = FunctionCollector() 
    @funcs.register 
    def get(): 
     return 'get' 
    @funcs.register 
    def put(): 
     return 'put' 
    return funcs 

C)(地元の人でつつく)とinspect.isfunctionと機能をフィルタリング:

このためのコードは次のようになります。 (通常は良いアイデア)

+0

Re: "a)関数をクラス定義に入れ、そのクラスへの参照を返すだけです。名前でアクセスされる関連関数のコレクションは、クラスのようにひどく匂いがします。このインスタンスでクラスを使用していない理由については、gavojaの答えに関する私のコメントを参照してください。あなたの説明をありがとう! –

関連する問題