Замыкания в Python

Под замыканием (closure) подразумевают функцию вместе со всеми внешними переменными, которые ей доступны.

В Python чаще всего, говоря "замыкание функции", имеют ввиду не саму эту функцию, а именно ее внешние переменные.

Пусть у нас есть следующая функция outer, в которой объявлена переменная i и внутренняя функция inner. В ней к i будет прибавляться единица. Чтобы переменная i корректно работала, добавляем к ней инструкцию nonlocal:

pythondef outer():
  i = 0
  def inner():
    nonlocal i
    i += 1
    print(i)
  
  return inner

Теперь давайте положим внешнюю функцию в переменную res и вызовем ее с круглыми скобками:

pythonres = outer()
res() # выведет 1

Давайте несколько раз вызовем res. После выполнения кода каждый раз счетчик i будет увеличиваться на единицу:

pythonres() # выведет 2
res() # выведет 3
res() # выведет 4

Однако здесь есть важный нюанс - каждый вызов функции outer будет увеличивать свой счетчик. Давайте первый вызов outer запишем в переменную res1, а второй вызов - в переменную res2. Затем последовательно выведем их в консоль:

pythonres1 = outer()
res1() # выведет 1
res1() # выведет 2
res1() # выведет 3

res2 = outer()
res2() # выведет 1
res2() # выведет 2
res2() # выведет 3
Задание 1

Дан следующий код:

pythondef outer():
  i = 10

  def inner():
    nonlocal i
    i -= 2
    print(i)
  return inner
  
res1 = outer()
res1()
res1()

res2 = outer()
res2()
res2()
res2()

Скажите, что выведется в консоль.

Задание 2

Сделайте функцию, каждый вызов которой будет вызывать следующее число Фибоначчи.

Задание 3

Сделайте функцию, которая будет выдавать случайное целое число в промежутке, но так, чтобы два раза подряд не было одинаковых.