Когда вы определяете функцию внутри другой функции и используете локальные переменные внешней функции во вложенной, вы создаете замыкание. Время жизни этих переменных "продляется" в особой области видимости enclosing даже после завершения работы внешней функции. Пример: make_adder возвращает функцию-прибавлятор. Объект из переменной a будет жить и работать даже после выхода из make_adder:
def make_adder(a):
def adder(x):
return a + x
return adder
plus_5 = make_adder(5)
print(plus_5(3)) # 8
Здесь я хочу коснуться одной популярной проблемы. Дело в том, что если мы создадим несколько функций внутри одного контекста, то они будут разделять одну область видимости enclosing. Рассмотрим пример создания трех функций в цикле:
def make_adders():
adders = []
for a in range(3):
def adder(x):
return a + x
adders.append(adder)
return adders
adders = make_adders()
for adder in adders:
print(adder(2)) # 4 4 4
Вместо функций прибавляющих разные числа от 0 до 2, мы получили 3 одинаковых функции, потому что внутри себя они поддерживают ссылку на одну и ту же переменную
a
, значение которой останется равным 2 после выполнения всего цикла целиком.Есть простой прием, помогающий "зафиксировать" значения переменной в моменте: достаточно добавить во вложенную функцию дополнительный аргумент со значением по умолчанию, равным нужной переменной
a=a
:def make_adders():
adders = []
for a in range(3):
def adder(x, a=a): # FIX!
return a + x
adders.append(adder)
return adders
adders = make_adders()
for adder in adders:
print(adder(2)) # 2 3 4
Еще лучше переименовать аргумент, чтобы избежать конфликтов имен и замечаний IDE, например, так:
def adder(x, that_a=a): # FIX!
return that_a + x
@python_job_interview