Python dynamic function generation

I am using PyQt and want to create a menu based on a list of strings.

The problem is that when I want to call 'addAction', it requires a callback function (for each line) that takes no arguments.

For simple menus, this will be good: for example.

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

However, I just want to use one function and pass an action string as an argument.

I am wondering if python can do something like this:

 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 

Here is my code snippet:

 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) 

Any suggestions that were highly appreciated

+4
source share
5 answers

You can use closure, for example:

 def printParam(parameter): def callback(): print "You selected %s" % parameter return callback for parameter in parameters: menu.addAction(parameter, printParam(parameter)) 
+4
source

functools.partial () allows you to provide some of the arguments in advance. This will allow you to make custom callbacks on your own.

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

Yes, this is called a partial application. The standard library has a class for partial use of the function, functools.partial . You write partial(f, x=7) or partial(f, 7) (since this is a positional argument and you do not need to skip any positional arguments) in your example.

+2
source

Use functools.partial to do the following (easier to demonstrate with a simple example):

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

If you always want to pass a specific argument, then as others pointed out, you can use closure:

 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 
+1
source
 def my_menu_func(self, string): # whatever your function does with that parameter 

then to set up the menu:

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

I tried using lamba directly inside addAction, but this creates a closure containing the variable x, so any subsequent local changes to x will change the parameter passed in the my_menu_func call. By doing this with eval, you can iterate over the list of lines and create entire menus, for example:

 for x in List_of_Menu_Items: 

do the same thing.

-1
source

All Articles