2011-07-13 126 views
4

我正在使用PyQt並希望基於字符串列表創建菜單。Python動態函數生成

問題是,當我想調用'addAction'時,它需要一個不帶任何參數的回調函數(對於每個字符串)。

對於簡單的菜單,這樣可以:

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

但是,我想只使用一個函數,並將'action string'作爲參數傳入。

我想知道如果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

「使用閉包」。閉包只是一個關閉封閉範圍內的自由變量的函數。 – 2011-07-13 17:03:18

+0

@pst:不,那是因爲'functools.partial'的存在而重新發明輪子。加上「封閉」是一個更廣泛的概念;) – delnan

+0

@delan :)你是什麼意思,封閉是一個更廣泛的概念? – 2011-07-13 19:07:25

回答

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) 

我試圖直接使用內部的addAction一個蘭巴,但爲了形成含有變量x的封閉,使任何後續的局部變化爲x將改變參數傳遞給my_menu_func。與EVAL做它,你可以遍歷字符串列表,並通過創建一個整個菜單例如:

for x in List_of_Menu_Items: 

做同樣的事情。

+0

爲何選擇投票?這是一個完全有效的答案。 – phkahler

+0

是不是我的投票,但我想這是因爲你的答案的'eval'部分。 – aukaost

+0

但是,如果沒有eval,它會創建一個函數,該函數調用my_menu_func,並引用局部變量x而不是值「Open」。所以在隨後的通話中,你不會得到你的想法。 – phkahler