Лямбда-функция, выходящая за пределы переменной
Я хотел поиграть с анонимными функциями, поэтому решил сделать простой простой поисковик. Вот он:
tests = []
end = int(1e2)
i = 3
while i <= end:
a = map(lambda f:f(i),tests)
if True not in a:
tests.append(lambda x:x%i==0)
print i
print tests
print "Test: "+str(i)
print str(a)
i+=2
То, что я нахожу, заключается в том, что каждый раз обращается к i
в lambda x:x%i==0
, тогда как я хочу, чтобы это было буквальное число. как я могу заставить его стать lambda x:x%3==0
вместо этого?
Ответы
Ответ 1
Вы можете "захватить" i
при создании лямбда
lambda x, i=i: x%i==0
Это установит i
в лямбда-контексте, равном какому-либо i
, когда он был создан. вы также можете сказать lambda x, n=i: x%n==0
, если хотите, это не совсем захватывает, но он получает то, что вам нужно.
Без этого, как вы видели, он будет искать i
в охватывающей области
Это проблема поиска, аналогичная следующему с определенными функциями:
i = "original"
def print_i1():
print(i) # prints "changed" when called below
def print_i2(s=i): #default set at function creation, not call
print(s) # prints "original" when called below
i = "changed"
print_i1()
print_i2()
Ответ 2
Проблема в том, что каждая из этих функций в tests
относится к переменной i
.
Чаще всего вы делаете это внутри функции, и в этом случае у вас есть переменная с областью для определения i
, которая хранится в закрытии, как это хорошо объясняется в Эти неприятные замыкания.
Но здесь он еще проще: i
- глобальная переменная, поэтому закрытие отсутствует. Функции скомпилированы для поиска i
в качестве глобальной переменной при запуске. Поскольку i
изменился, функции будут видеть измененное значение при их запуске. Просто как это.
Традиционный способ (который работает как для закрытия, так и для globals) нежно известен как "взлом по умолчанию", даже если это не хак. (См. объяснение в FAQ). Ответ Райана Хаинга объясняет, как это сделать:
lambda x, i=i: x%i==0
Создает параметр с именем i
со значением по умолчанию, равным значению i
во время создания функции. Затем внутри функции, когда вы обращаетесь к параметру i
, и получаете это значение.
По-другому, что может показаться более знакомым, если вы используете такие языки, как JavaScript, - это создать функцию создания функций и передать значение i
в качестве аргумента функции создания функции, как в user2864740 Ответ:
(lambda i: lambda x: x%i)(i)
Это позволяет избежать "загрязнения" сигнатуры функции дополнительным параметром (что кто-то может случайно передать аргумент), но ценой создания и вызова функции без уважительной причины.
Третий способ - использовать partial
. В тех случаях, когда все, что вы пытаетесь сделать, частично применяет функцию, использование partial
вместо определения функции-обертки как lambda
может быть более чистым.
К сожалению, в этом случае функция скрыта внутри оператора, а функция operator.mod
, которая предоставляет ее, не принимает аргументы ключевого слова, так что вы не можете с пользой частично ее второй операнд. Таким образом, это плохое решение в этом случае. Если вы действительно этого хотели, вы могли бы просто написать обертку, которая ведет себя лучше и partial
, что:
def opmod(a, b):
return a % b
partial(operator.mod, b=i)
В этом случае, я думаю, вам лучше с другими решениями; просто держите это в своей голове для случаев, когда это уместно.
Ответ 3
Создайте новую функцию, которая возвращает лямбда. Затем назовите это, передав i
в качестве аргумента. Это создаст новую область привязки.
def make_test (i):
# this i refers to the parameter (which evaluates to the /value/ passed)
return lambda x: x%i==0
# ..
# the /value/ resulting from evaluating the variable is passed
tests.append(make_test(i))