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)


arrow_upward
14
arrow_downward

Это именно то, для чего предназначен 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

arrow_upward
2
arrow_downward

Вы можете создать функцию более высокого порядка, чтобы обернуть свою функцию 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

arrow_upward
1
arrow_downward

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

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

arrow_upward
0
arrow_downward

Если у вас есть функции с определенными параметрами (которые не зависят от таких вещей, как 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

arrow_upward
0
arrow_downward

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

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