Очень часто lambda-выражения используются для передачи дополнительных данных в обработчик события (для простоты я опустил вызовы функций pack и mainloop в следующем фрагменте):
def handler(A, B): # обычно вызывается без аргументов
…использование A и B…
X = 42
Button(text=’ni’, command=(lambda: handler(X, ‘spam’))) # lambda добавляет # аргументы
Библиотека tkinter вызывает обработчики command, не передавая им никаких аргументов, тем не менее с помощью такого lambda-выражения можно создать косвенную анонимную функцию, которая будет играть роль оболочки для действительного обработчика и передавать ему информацию, существовавшую в момент создания графического интерфейса. Вызов фактического обработчика откладывается, благодаря чему мы получаем возможность добавлять необходимые аргументы. В данном случае значение глобальной переменной X и строка “spam” будут переданы в аргументах A и B даже при том, что библиотека tkinter вызывает функции обратного вызова без аргументов. Таким образом, инструкция lambda может использоваться для отображения вызова без аргументов в вызов с аргументами, которые поставляются самим lambda-выражением.
Если такой синтаксис приводит вас в замешательство, запомните, что такое lambda-выражение, как показано выше, обычно может быть реализовано в виде простой инструкции def. Вторая функция в следующем фрагменте выполняет ту же операцию, что и lambda-выражение в предыдущем фрагменте, — ссылка на нее в операции создания кнопки фактически откладывает вызов действительного обработчика, чтобы ему можно было передать дополнительные аргументы:
def handler(A, B): # обычно вызывается без аргументов
…использование A и B…
X = 42
def func(): # косвенная функция-обертка, добавляющая аргументы
handler(X, ‘spam’)
Button(text=’ni’, command=func)
Чтобы более отчетливо понять, зачем необходимы отложенные вызовы, обратите внимание, что произойдет, если в операцию создания кнопки вставить вызов самого обработчика с аргументами без использования lambda-выражения или другой промежуточной функции, — обработчик будет вызван немедленно, в момент создания кнопки, а не когда на ней будет выполнен щелчок. Именно для того чтобы отложить вызов обработчика, обращение к нему требуется обернуть промежуточной функцией:
def handler(name): print(name)
Button(command=handler(‘spam’)) # ОШИБКА: обработчик будет вызван немедленно!
Использование lambda-выражений или ссылок на вызываемые объекты позволяет отложить вызов обработчика до появления события. Например, использование lambda-выражения для передачи дополнительных данных с помощью определения встроенной функции, вызов которой откладывается:
def handler(name): print(name)
Button(command=(lambda: handler(‘spam’))) # OK: обертывание lambda—выражением # откладывает вызов
всегда эквивалентно более длинной и, по мнению некоторых, менее удобной форме с двумя функциями:
def handler(name): print(name)
def temp():
handler(‘spam’)
Button(command=temp) # OK: ссылка на функцию, а не ее вызов
Нам достаточно применять только какой-то один прием — либо lambda— выражение без аргументов, либо ссылку на вызываемый объект, не принимающий аргументов, но не оба сразу. Бессмысленно использовать lambda-выражение, просто вызывающее функцию, которая не принимает дополнительных аргументов, так как в этом случае будет выполняться один лишний вызов:
def handler(name): print(name)
def temp():
handler(‘spam’)
Button(command=(lambda: temp())) # БЕССМЫСЛЕННО: добавляет лишний вызов!
Как будет показано далее, допускается также использовать ссылки на другие вызываемые объекты, такие как связанные методы и вызываемые экземпляры классов, которые сохраняют необходимую информацию в своих атрибутах. Если они не принимают аргументов, их имена можно просто указывать на этапе создания виджетов и их не требуется обертывать лишними lambda-выражениями.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011