Python — функция сохранения с параметрами

Я использую Tkinter для простой викторины. Есть несколько кнопок, по одной для каждого ответа, и я хотел бы запустить функцию checkAnswer с определенными параметрами при нажатии на нее.

Если бы я использовал что-то вроде следующего:

self.option1 = Button(frame, text="1842", command=self.checkAnswer(question=3, answer=2))

Затем он запускал checkAnswer и использовал то, что возвращал (ничего).

Есть ли простой способ сохранить параметры в конструкторе кнопок?


person Cheezey    schedule 21.05.2012    source источник


Ответы (5)


Это именно то, для чего предназначен functools.partial():

>>> import functools
>>> print_with_hello = functools.partial(print, "Hello")
>>> print_with_hello("World")
Hello World
>>> print_with_hello()
Hello

partial() возвращает новую функцию, которая ведет себя так же, как и старая, но с любыми переданными вами аргументами, поэтому в вашем случае:

import functools

...

self.option1 = Button(frame, text="1842", command=functools.partial(self.checkAnswer, question=3, answer=2))
person Gareth Latty    schedule 21.05.2012

Вы можете создать функцию более высокого порядка, чтобы обернуть свою функцию checkAnswer. Это позволит вам вернуть функцию, которая не требует никаких параметров и, следовательно, может использоваться в качестве обратного вызова.

Например:

def makeCheckAnswer(self, **kwargs)
    return lambda: self.checkAnswer(**kwargs)

Это сделает инициализацию вашей кнопки:

self.option1 = Button(frame, text="1842", command=self.makeCheckAnswer(question=3, answer=2))
person jtmoulia    schedule 21.05.2012
comment
Я не буду -1, но это изобретение велосипеда. functools.partial() делает это. - person Gareth Latty; 22.05.2012
comment
Это аккуратное и лучшее решение. Спасибо! - person jtmoulia; 22.05.2012

Безусловно, проще всего просто использовать лямбда на месте

self.option1 = Button(frame, text="1842", 
    command=lambda: self.checkAnswer(question=3, answer=2))

Хотя в подобных, но немного более сложных случаях вам действительно следует использовать фабрику функций, такую ​​как

def answerCheckerFactory(self, question, answer):
    def checker():
        return self.checkAnswer(question, answer)

    return checker

    ...
    self.option1 = Button(frame, text="1842", 
        command=self.answerCheckerFactory(question=3, answer=2))

потому что это гарантирует, что вы передаете правильные аргументы (например, не quetsion (так в оригинале)); обратите внимание на отличие от functools.partial, которое позволяет вам неправильно набирать аргументы функции и получать исключение только при нажатии на кнопку;)

Кроме того, жесткое кодирование вопросов/ответов в коде кнопки кажется неправильным...

person Antti Haapala    schedule 21.05.2012
comment
Я совершенно не согласен, это менее понятно и изобретает велосипед. - person Gareth Latty; 22.05.2012
comment
Я бы не стал это жестко кодировать, это был просто пример. Я не возражаю, если я получу отложенное исключение. - person Cheezey; 22.05.2012

Если у вас есть функции с определенными параметрами (которые не зависят от таких вещей, как text.get()), вы можете использовать для них обертки, что было бы самым простым способом сделать это, кроме функций лямбда. Например:

def option1wrapper():
    ClassName.checkAnswer(question=3,answer=2)
self.option1 = Button(frame, text="1842", command=option1wrapper)
person IT Ninja    schedule 21.05.2012

Только вам нужно использовать лямбда-функцию, подобную этой

self.option1 = lambda: Button(frame, text="1842", command=self.checkAnswer(question=3, answer=2))
person Tlaloc-ES    schedule 06.08.2021