Генерация динамических функций Python - PullRequest
4 голосов
/ 13 июля 2011

Я использую PyQt и хочу создать меню на основе списка строк.

Проблема в том, что когда я хочу вызвать 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)

Любые предложения с благодарностью

Ответы [ 5 ]

7 голосов
/ 13 июля 2011

functools.partial () позволяет вам заранее указать некоторые аргументы.Это позволит вам сделать пользовательские обратные вызовы, как вы хотите.

>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18
4 голосов
/ 13 июля 2011

Вы можете использовать замыкание, как это:

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

for parameter in parameters:
    menu.addAction(parameter, printParam(parameter))
2 голосов
/ 13 июля 2011

Да, это называется частичным применением. В стандартной библиотеке есть класс для частичного применения функции: functools.partial. Вы бы написали partial(f, x=7) или partial(f, 7) (поскольку это позиционный аргумент и вам не нужно пропускать позиционные аргументы) в вашем примере.

1 голос
/ 13 июля 2011

Используйте 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 голосов
/ 13 июля 2011
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:

сделать то же самое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...