На этом шаге мы рассмотрим особенности реализации этой операции.
Вы определили анонимную функцию, используя lambda, но вы также хотите захватить значения некоторых переменных во время определения.
Рассмотрим поведение следующей программы:
>>> x = 10 >>> a = lambda y: x + y >>> x = 20 >>> b = lambda y: x + y >>>
А теперь задайте себе вопрос: какими будут значения a(10) и b(10)? Если вы думаете, что 20 и 30, то ошибаетесь:
>>> a(10) 30 >>> b(10) 30 >>>
Проблема в том, что значение х, используемое в lambda-выражении, является свободной переменной, которая связывается во время выполнения, а не во время определения. Так что значение х в lambda-выражениях будет таким, каким ему случится быть во время выполнения. Например:
>>> x = 15 >>> a(10) 25 >>> x = 3 >>> a(10) 13 >>>
Если же вы хотите, чтобы анонимная функция захватывала значение во время определения и сохраняла его, используйте значение по умолчанию:
>>> x = 10 >>> a = lambda y, x=x: x + y >>> x = 20 >>> b = lambda y, x=x: x + y >>> a(10) 20 >>> b(10) 30 >>>
Проблема, которую мы рассматриваем в этом рецепте, возникает, когда случается перемудрить с lambda-функциями. Например, вы можете ожидать, что при создании списка lambda-выражений с использованием генератора списка или цикла lambda-функции запомнят итерационные переменные во время определения. Например:
>>> funcs = [lambda x: x + n for n in range(5)] >>> for f in funcs: print(f(0)) 4 4 4 4 4 >>>
Обратите внимание, что все функции считают, что n имеет последнее значение, полученное в ходе итераций. Теперь сравните со следующим:
>>> funcs = [lambda x, n=n: x + n for n in range(5)] >>> for f in funcs: print(f(0)) 0 1 2 3 4 >>>
Как вы видите, теперь функции захватывают значения n во время определения.
На следующем шаге мы рассмотрим, как заставить вызываемый объект с N аргументами работать так же, как вызываемый объект с меньшим количеством аргументов.