Вывод результатов, возвращаемых диалогами, и передача данных обработчикам с помощью lambda-выражений

vyvod rezultatov vozvrashhaemyh dialogami i peredacha dannyh obrabotchikam s pomoshhju lambda vyrazhenij Экскурсия по tkinter, часть 1

Демонстрационная панель запуска диалогов выводит стандартные диалоги и может быть использована для вывода других диалогов простым изменением импортируемого модуля dialogTable. Однако в существующем виде этот пример только показывает диалоги, и было бы неплохо посмотреть на возвращаемые ими значения, чтобы знать, как использовать их в сценариях. В примере 8.10 добавлен вывод результатов стандартных диалогов в стандартный поток вывода stdout.

Пример 8.10. PP4E\Gui\Tour\demoDlg-print.py

то же, что и предыдущий пример, но выводит значения, возвращаемые диалогами; lambda-выражение сохраняет данные из локальной области видимости для передачи их обработчику (обработчик события нажатия кнопки обычно не получает аргументов, а автоматические ссылки в объемлющую область видимости некорректно работают с переменными цикла) и действует подобно вложенной инструкции def, такой как: def func(key=key): self.printit(key)

from tkinter import * # импортировать базовый набор виджетов

from dialogTable import demos # обработчики событий от кнопок

from quitter import Quitter # прикрепить к себе объект quit

class Demo(Frame):

def __init__(self, parent=None):

Frame.__init__(self, parent)

self.pack()

Label(self, text=”Basic demos”).pack() for key in demos:

func = (lambda key=key: self.printit(key))

Button(self, text=key, command=func).pack(side=TOP, fill=BOTH) Quitter(self).pack(side=TOP, fill=BOTH)

def printit(self, name):

print(name, ‘returns =>’, demos[name]()) # извлечь, вызвать, вывести

if __name__ == ‘__main__’: Demo().mainloop()

Этот сценарий создает то же самое главное окно панели кнопок, но обратите внимание, что обработчик события теперь является анонимной функцией, созданной с помощью lambda-выражения, а не прямой ссылкой на вызов диалога в словаре demos, импортированном из модуля dialogTable:

#  задействовать поиск значения в объемлющей области видимости func = (lambda key=key: self.printit(key))

Мы уже говорили о такой возможности в предыдущей главе, но здесь мы впервые использовали lambda-выражение подобным образом, поэтому разберемся в том, что происходит. Так как обработчики событий нажатий кнопок вызываются без аргументов, то при необходимости передать обработчику дополнительные данные для него нужно создать оболочку в виде объекта, который запомнит эти дополнительные данные и передаст их фактическому обработчику. В данном случае при нажатии кнопки вызывается функция, создаваемая lambda-выражением, — промежуточная функция, сохраняющая информацию из объемлющей области видимости. Благодаря этому действительный обработчик, printit, получит дополнительный аргумент name и выполнит действия, связанные с нажатой кнопкой, несмотря на то, что этот аргумент не был передан самой библиотекой tkinter. Фактически lambda-выражение сохраняет и передает информацию о состоянии.

Заметьте, однако, что в теле функции, создаваемой этим lambdaвыражением, используются ссылки на значения self и key, находящиеся в объемлющей области видимости. Во всех последних версиях Python ссылка на self действует автоматически, в соответствии с правилами поиска значений в объемлющих областях видимости, но значение key необходимо передать явно, в виде аргумента со значением по умолчанию, иначе все функции, сгенерированные lambda-выражением, получат одно и то же значение — которое получит переменная key в последней итерации цикла. Как мы узнали в главе 7, ссылки на переменные в объемлющей области видимости разрешаются в момент вызова вложенной функции, а ссылки на значения по умолчанию — в момент создания вложенной функции. Так как значение self не изменится после создания функции, мы можем довериться правилам поиска только этого имени, но не переменной цикла key.

В прежних версиях Python требовалось явно передавать любые значения из объемлющей области видимости в виде аргументов со значениями по умолчанию, используя любой из двух следующих приемов:

#  использовать простые аргументы со значениями по умолчанию func = (lambda self=self, name=key: self.printit(name))

#  использовать связанный метод по умолчанию

func = (lambda handler=self.printit, name=key: handler(name))

В настоящее время для получения значения self можно использовать более простой прием автоматических ссылок в объемлющую область видимости, однако для передачи значения переменной key по-прежнему требуется использовать значение по умолчанию аргумента (а кроме того, передачу данных в виде значений по умолчанию можно встретить в давно написанных сценариях на языке Python).

Обратите внимание, что круглые скобки вокруг lambda-выражений здесь не являются обязательными — я добавляю их, потому что предпочитаю визуально отделять lambda-выражения от окружающего программного кода (ваши предпочтения могут отличаться от моих). Отметьте также, что здесь lambda-выражение можно заменить вложенной инструкцией def. Однако в отличие от инструкции def lambda-выражение может появляться внутри вызова конструктора Button, потому что это выражение и ему не требуется присваивать имя. Следующие две формы совершенно равноценны:

for (key, value) in demos.items():

func = (lambda key=key: self.printit(key)) # может вкладываться в вызов

# Button()

for (key, value) in demos.items():

def func(key=key): self.printit(key) # а инструкция def нет

Здесь можно также использовать вызываемый объект класса, который сохраняет состояние в виде атрибутов экземпляра (смотрите подсказку в учебном примере __call__ главы 7). Но как правило, если нужно, чтобы результат lambda-выражения в последующих вызовах использовал переменные из объемлющей области, просто используйте их имена и позвольте интерпретатору самому сохранять значения для последующего использования или передайте их в качестве значений по умолчанию, чтобы обеспечить сохранение значений на этапе создания функции. Последний способ необходим, только если используемая переменная может изменить значение перед тем, как произойдет вызов обработчика.

Если запустить этот сценарий, он создаст то же окно (рис. 8.11) и дополнительно будет выводить значения, возвращаемые диалогами, в стандартный поток вывода. Ниже приводится вывод сценария после щелчков мышью на всех кнопках в главном окне и выбора в каждом диалоге обеих кнопок Cancel/No и OK/Yes:

C:\\PP4E\Gui\Tour> python demoDlg-print.py

Color returns => (None, None)

Color returns => ((128.5, 128.5, 255.99609375), ‘#8080ff’)

Query returns => no

Query returns => yes

Input returns => None

Input returns => 3.14159

Open returns =>

Open returns => C:/Users/mark/Stuff/Books/4E/PP4E/dev/Examples/PP4E/Launcher.py Error returns => ok

Теперь, когда были показаны результаты вызова всех диалогов, я хочу продемонстрировать фактическое использование одного из них.

Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий