56. Замыкания и оператор nonlocal в Python
В предыдущем уроке мы разобрались с областью видимости в Python. Сейчас будем разбираться с замыканиями. Тема достаточно непростая для понимания начинающему программисту. Если вы дошли до этого урока, то обязательно разберетесь с замыканиями, не сомневайтесь.
Замыкание (англ. closure) – это функция, которая ссылается на переменные из внешней функции в области видимости которой она находится. Обычной функции доступны свои аргументы и глобальные переменные – в этом различие.
Внутренняя функция создается при каждом выполнении внешней. Таким образом, каждый раз создается новый экземпляр с обновленными ссылками на переменные внешней функции. Эти ссылки действительны до тех пор, пока выполняется вложенная функция.
Согласен, немного непонятно, поэтому рассмотрим пример:
def mult(num1):
def inner(num2):
return num1 * num2
return inner
mult_by_2 = mult(2) # ссылается на inner()
print(mult_by_2(2)) # => 4
В данном примере функция inner()
является замыканием. Так же необходимо понимать в какой момент происходит запоминание значения. Для этого рассмотрим пример:
def mult(num1):
x = 1
def inner(num2):
return num1 * num2 + x
x = 10
return inner
mult_by_2 = mult(2)
print(mult_by_2(2))
На первый взгляд можно предположить, что результатом будет число 5
, но нет.
Рассмотрим следующий пример:
def mult(num1):
a = 10
def inner(num2):
a += 1
return num1 * num2 + a
return inner
mult_by_2 = mult(2)
print(mult_by_2(2))
Выполнение этого кода вызовет ошибку. Переменная a
является свободной, поэтому чтобы ее менять с сохранением измененного значения между вызовами, следует использовать nonlocal
:
def mult(num1):
a = 10
def inner(num2):
nonlocal a
a += 1
return num1 * num2 + a
return inner
Для обыкновенного присваивания значения переменной a
, nonlocal
использовать не нужно. Например, nonlocal
можно использовать следующим способом:
def up(num1):
a = num1
def inner():
nonlocal a
a -= 1
return a
return inner
up_one = up(10)
print(up_one()) # => 9
print(up_one()) # => 8
print(up_one()) # => 7
print(up_one()) # => 6
Замыкания из-за умения запоминать значения свободных переменных чем-то напоминают классы (о них мы будем говорить позднее). Далее пример:
def calc(a, b):
def add():
return a + b
def sub():
return a - b
calc.add = add
calc.sub = sub
return calc
calc_obj = calc(10, 2)
print(calc_obj.add()) # => 12
print(calc_obj.sub()) # => 8
В этом уроке познакомились с замыканиями в Python и ситуациями, когда следует использовать оператор nonlocal
. Далее будем говорить об аргументах функции.