TGViewer
Библиотека Python разработчика | Книги по питону Библиотека Python разработчика | Книги по питону @bookpython · 18.1K subscribers
Post #4153 907
Ловушка замыканий: Почему ваши лямбды в цикле сломаны (Late Binding)

Вы пишете код, который генерирует список функций (например, колбэки для кнопок в UI или динамические фильтры). Кажется, что все логично, но на выходе получаете сюрприз.

Проблемный код:


# Хотим создать 3 функции, которые возвращают 0, 1 и 2 соответственно
funcs = []
for i in range(3):
funcs.append(lambda: i)

# Проверяем
results = [f() for f in funcs]
print(results)
# Ожидание: [0, 1, 2]
# Реальность: [2, 2, 2]



Почему так происходит?
Это называется Late Binding (позднее связывание).
В Python замыкания (closures) захватывают переменные по ссылке, а не по значению.
Когда вы объявляете lambda: i, Python не сохраняет текущее число 0, 1 или 2. Он сохраняет инструкцию: «когда меня вызовут, пойди в локальную область видимости, найди переменную с именем i и возьми ее значение».

К моменту, когда вы начинаете вызывать функции из списка results, цикл for уже завершился. Переменная i в этой области видимости навсегда осталась равной 2. Все три лямбды смотрят на одну и ту же переменную i.

Как лечить?

Есть два каноничных способа заставить Python запомнить значение «здесь и сейчас».

1. Аргумент по умолчанию (Hack way)
Значения аргументов по умолчанию вычисляются в момент определения функции.


funcs = []
for i in range(3):
# i=i создает локальную переменную i внутри функции
# и присваивает ей текущее значение i из цикла
funcs.append(lambda i=i: i)



Это работает быстро, но выглядит немного грязно и может сбить с толку линтеры или коллег.

2. functools.partial (Enterprise way)
Более чистый и явный способ. partial создает новый callable-объект, «замораживая» переданные аргументы.


from functools import partial

funcs = []
for i in range(3):
# Здесь значение i фиксируется жестко
funcs.append(partial(lambda x: x, i))



Где это стреляет в реальной жизни?

- Генерация command для кнопок в Tkinter/PyQt.
- Динамическое создание task в asyncio циклах.
- Патчинг тестов в циклах.

Не дайте переменным пережить свое время.

#python #internals #functionalprogramming #gotchas

📲 Мы в MAX

👉@BookPython
  • 👍 3
  • ❤ 2
  • 🔥 2
More from @bookpython
  1. Sep 21, 202610 полезных библиотек для Python-разработчика Сегодня я подготовил для вас подборку из 10…
  2. Sep 16, 2026Если у вас есть ресурсоемкая задача для процессора и вы хотите использовать все доступные…
  3. Sep 15, 2026Protocol vs ABC: Утиная типизация на стероидах (Static Duck Typing) В классическом ООП (Ja…
  4. Sep 14, 2026⚡ Как ускорить код на Python? Используем map, filter, reduce ⚡ Привет, друзья! Сегодня рас…
  5. Sep 9, 2026Профилируем Python в продакшене: почему cProfile не подходит, и чем хорош py-spy Когда на…
  6. Sep 8, 2026📌 Декораторы в Python: как они работают и зачем нужны? Сегодня я покажу вам, как работают…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →