Когда выполняются операторы функций, они привязываются к своей (лексической) области видимости.
В вашем фрагменте лямбды привязаны к глобальной области видимости, потому что for
комплекты не выполняются в Python как независимые единицы измерения. В конце цикла for
, num
ограничен в пределах объема. Демонстрация:
for num in range(1, 6):
pass
assert num == 5 # num is now bound in the enclosing scope
Таким образом, когда вы связываете идентификаторы в цикле for
, вы фактически манипулируете включающей областью действия.
for num in range(1, 6):
spam = 12
assert num == 5 # num is now bound in the enclosing scope
assert spam == 12 # spam is also bound in the enclosing scope
То же самое для списочных представлений:
[num for num in range(1, 6)]
assert num == 5
Сногсшибательно, я знаю. В любом случае, благодаря нашим новым знаниям, мы можем определить, что создаваемые лямбды ссылаются на (один) идентификатор num
, связанный в пределах объема. Это должно иметь больше смысла:
functions = []
for number in range(1, 6):
def fun():
return number
functions.append(fun)
assert all(fun() == 5 for fun in functions)
assert all(fun() is number for fun in functions)
А вот самая крутая часть, которая демонстрирует это еще больше:
# Same as above -- commented out for emphasis.
#functions = []
#for number in range(1, 6):
# def fun():
# return number
# functions.append(fun)
#assert all(fun() == 5 for fun in functions)
#assert all(fun() is number for fun in functions)
number = 6 # Rebind 6 in the scope and see how it affects the results.
assert all(fun() == 6 for fun in functions)
Таким образом, решение этой проблемы, конечно же, заключается в создании новой ограждающей области для каждого number
, который вы хотите связать. В Python вы можете создавать новые вмещающие области с модулями, классами и функциями. Обычно функцию используют для создания новой области видимости для другой функции.
В Python замыкание - это функция, которая возвращает другую функцию . Вроде как конструктор функций. Проверьте get_fun
в следующем примере:
def get_fun(value):
""":return: A function that returns :param:`value`."""
def fun(): # Bound to get_fun's scope
return value
return fun
functions = []
for number in range(1, 6):
functions.append(get_fun(number))
assert [fun() for fun in functions] == range(1, 6)
Поскольку get_fun
является функцией, она имеет собственную внутреннюю область видимости. Каждый раз, когда вы вызываете get_fun
со значением, создается небольшая таблица, чтобы отслеживать привязки внутри нее; то есть он говорит: «В этом объеме идентификатор value
указывает на то, что было передано». Эта область исчезает в конце выполнения функции, если только у нее нет причин задерживаться.
Если вы возвращаете функцию из области действия, это хорошая причина для того, чтобы части "таблицы областей действия" зависали - эта функция, которую вы возвращаете, может ссылаться на вещи из этой таблицы области действия при последующем вызове на. По этой причине, когда fun
создается в get_fun
, Python сообщает fun
о таблице областей действия get_fun
, которая fun
всегда под рукой, когда это необходимо.
Вы можете узнать больше о деталях и технической терминологии (которую я немного смягчил) в Документах Python по модели исполнения . Вы также можете посмотреть на те части окружения, на которые ссылается функция с помощью print fun.__closure__
. В приведенном выше примере мы видим ссылку на value
, которая является целым числом:
# Same as before, commented out for emphasis.
#functions = []
#for number in range(1, 6):
# functions.append(get_fun(number))
#assert [fun() for fun in functions] == range(1, 6)
print functions[0].__closure__
# Produces: (<cell at 0x8dc30: int object at 0x1004188>,)