Глава 16 · Создаём классные приложения с Tkinter

Архитектура приложения: логика отдельно от виджетов

Лучшая GUI-логика — та, которую можно протестировать, даже не открывая окно.

Чистая логика отдельно от виджетов

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

chistaya_logika.py
def calculate_tip(amount, percent, people):
    total_tip = amount * percent / 100
    return total_tip / people

print(calculate_tip(1000, 15, 2))   # 75.0
callback (обработчик кнопки)
прочитать значения виджетов
проверить (раздел 16.23)
чистая функция — без единого виджета внутри
calculate_tip(...)
показать результат в виджете
Callback — тонкий слой-посредник; вся логика вычисления живёт вне интерфейса.
Callback не должен разрастаться
Если обработчик кнопки занимает сотню строк с вложенными вычислениями — вероятно, часть этой логики стоит вынести в отдельную функцию, которую можно понять и проверить без запуска окна.

Чуть глубже: ловушка позднего связывания в lambda

Debug Lab 4: Все кнопки в цикле «запоминают» одно и то же значение
pozdnee_svyazyvanie.py
for i in range(3):
    ttk.Button(root, text=str(i), command=lambda: print(i)).pack()
# Все три кнопки выведут одно и то же число — последнее значение i (2),
# а не 0, 1, 2 соответственно.
Что видно на экране

lambda: print(i) обращается к переменной i из окружающей области видимости в момент клика, а не в момент создания кнопки (глава 13, замыкания). К моменту клика цикл уже закончился, и i равно своему последнему значению — 2 — для всех трёх кнопок сразу.

Исправленный код
pozdnee_svyazyvanie_fixed.py
for i in range(3):
    ttk.Button(root, text=str(i), command=lambda i=i: print(i)).pack()
# i=i создаёт значение по умолчанию, зафиксированное ИМЕННО в момент создания lambda
proverka_lambda_fix.py
callbacks = []
for i in range(3):
    callbacks.append(lambda i=i: i)

values = [cb() for cb in callbacks]
print(values)   # [0, 1, 2]
Практика: чистая логика и ловушка lambda
Проверяется без tkinter — на функциях и замыканиях
Открыть практику →