2011-07-13 12 views
4

PyQtを使用していて、文字列のリストに基づいてメニューを作成したいとします。Pythonの動的関数生成

問題は、私は「addAction」を呼び出したいとき、それは引数を取りません(各文字列の)コールバック機能を必要とすることです。

単純なメニューの場合、これは問題ありません。

menu.addAction("Open", self.open) 
menu.addAction("Exit", self.quit) 

しかし、私は単なる関数を使用して、引数として 'アクション文字列'を渡したいと考えています。

Pythonはこのような何か行うことができます場合、私は疑問に思って:ここで

def f(x, y): 
    print x + 2*y 



# These 2 variables are of type: <type 'function'> 
callback1 = f 
callback2 = f(x=7, *) 
# NB: the line above is not valid python code. 
# I'm just illustrating my desired functionality 

print callback1(2,5) # prints 12 
print callback2(5) # prints 17 

は私のコードスニペットです:

def printParam(parameter): 
    print "You selected %s" % parameter 


# parameters = list of strings 

menu_bar = QMenuBar() 
menu = menu_bar.addMenu('Select Parameter') 
for parameter in parameters: 
    # This line will not work because the action does not pass in 
    # any arguments to the function 
    menu.addAction(parameter, printParam) 

任意の提案を大幅に

+0

"閉鎖を使用する"。クロージャーは、単に囲みスコープ内のフリー変数をクローズする関数です。 –

+0

@pst:いいえ、それは 'functools.partial'の存在を考えればホイールを再発明するでしょう。プラス "クロージャー"ははるかに広い概念です;) – delnan

+0

@delan :)クロージャーはより広い概念であるとはどういう意味ですか? –

答えて

4

あなたに感謝しましたthiのようなクロージャを使うことができますS:

def printParam(parameter): 
    def callback(): 
     print "You selected %s" % parameter 
    return callback 

for parameter in parameters: 
    menu.addAction(parameter, printParam(parameter)) 
+1

前に書いたように、関数を部分的に適用するだけでよい場合は、 'functools.partial'を使うだけで、もっと短く(間違いなく)簡単になります。より複雑なロジックが必要な場合には、このような高次関数が良い選択です。 – delnan

+0

はい、 'functools.partial'はここで仕事をします。しかし、閉鎖について美しいものがあります。 –

1

使用functools.partial次の操作を実行するために(簡単にあなたの簡単な例を示すため):

import functools 
callback2=functools.partial(f, 7) 
callback3=functools.partial(f, y=5) #if you want to pass `y` instead of `x` 

あなたが常に特定の引数を渡したい場合は、他の人のように、あなたは可能性が指摘されてきましたクロージャを使用します。

def f(x): 
    def g(y): 
     print x + 2*y 
    return g 
callback1=f 
callback2=f(7) 
callback1(2)(5) #prints 12 
callback2(5) #prints 17 
+0

これで 'callback2(y = 5)'と書かなければなりません。そうでなければ、 'callback2(5)'を呼び出すときに 'x'に対して複数の値を受け取るというエラーが発生します。この場合は 'functools.partial(f、7)'と書くのが簡単です。 –

+0

@isbadawi注目され、修正されました。ありがとう。 – murgatroid99

7

functools.partial()はあなたが先に時間の引数の一部を供給することができます。必要に応じてカスタムコールバックを作成することができます。

>>> from functools import partial 
>>> basetwo = partial(int, base=2) 
>>> basetwo.__doc__ = 'Convert base 2 string to an int.' 
>>> basetwo('10010') 
18 
2

はい、部分適用と呼ばれます。標準ライブラリには、部分的に関数を適用するクラスがあります(functools.partial)。あなたの例では、partial(f, x=7)またはpartial(f, 7)と書くことができます(これは定位置の引数であり、定位置の引数をスキップする必要はありません)。

-1
def my_menu_func(self, string): 
    # whatever your function does with that parameter 

は、メニューを設定する:

x = "Open" 
a = eval("lambda : my_menu_func('"+x+"')") 
menu.addAction(x, a) 

IはaddAction内に直接ラムダを使用して試みたが、それは、変数xを含むクロージャを形成するので、Xに後続のローカルな変更を変化させますmy_menu_funcの呼び出しで渡されたパラメータ。これをevalで実行すると、文字列のリストを繰り返し処理し、メニュー全体を作成することができます。たとえば、

for x in List_of_Menu_Items: 

と同じことをします。

+0

なぜ賛成投票ですか?それは完全に有効な答えです。 – phkahler

+0

私の投票ではありませんでしたが、答えの「評価」の部分が原因だと思います。 – aukaost

+0

しかし、評価なしでは、値 "Open"の代わりにローカル変数xへの参照でmy_menu_funcを呼び出す関数を作成します。その後の呼び出しでは、あなたはあなたの考えを得ることができません。 – phkahler

関連する問題