Этот вопрос затрагивает очень вонючую часть «известного» и «очевидного» синтаксиса Python - то, что имеет приоритет, лямбда или для понимания списка.
Я не думаю, что целью ОП было создание списка квадратов от 0 до 9. Если бы это было так, мы могли бы дать еще больше решений:
squares = []
for x in range(10): squares.append(x*x)
- это хороший старый способ императивного синтаксиса.
Но это не главное. Дело в том, что W (hy) TF - это неоднозначное выражение, настолько нелогичное? И в конце у меня для вас идиотский случай, так что не отклоняйте мой ответ слишком рано (он был у меня на собеседовании).
Итак, понимание ОП вернуло список лямбд:
[(lambda x: x*x) for x in range(10)]
Это, конечно, всего 10 различных копий функции возведения в квадрат, см .:
>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]
Обратите внимание на адреса памяти лямбд - они все разные!
Конечно, вы можете иметь более «оптимальную» (ха-ха) версию этого выражения:
>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]
Видеть? 3 раза такая же лямбда.
Обратите внимание, что я использовал _в качестве forпеременной. Это не имеет ничего общего с xin lambda(это лексически затенено!). Возьми?
Я опускаю обсуждение, почему приоритет синтаксиса не таков, что все это означало:
[lambda x: (x*x for x in range(10))]
который может быть: [[0, 1, 4, ..., 81]]или [(0, 1, 4, ..., 81)], или который я считаю наиболее логичным , это будет элемент listиз 1 - generatorвозвращающий значения. Это просто не тот случай, язык так не работает.
Но что, если ...
Что делать, если вы не затеняете forпеременную и не используете ее в своей переменной lambda???
Ну, тогда случается дерьмо. Посмотри на это:
[lambda x: x * i for i in range(4)]
это значит конечно:
[(lambda x: x * i) for i in range(4)]
НО это не значит:
[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]
Это просто безумие!
Лямбды в понимании списка являются закрытием по всему объему этого понимания. Лексическое закрытие, поэтому они относятся к iпо ссылке, а не его значение , когда они были оценены!
Итак, это выражение:
[(lambda x: x * i) for i in range(4)]
Примерно эквивалентно:
[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]
Я уверен, что мы могли бы увидеть больше здесь, используя декомпилятор Python (я имею в виду, например, disмодуль), но для обсуждения Python-VM-agnostic этого достаточно. Так много для вопроса собеседования.
Теперь, как сделать listиз лямбда-множителей, которые действительно умножаются на последовательные целые числа? Ну, как и в случае с принятым ответом, нам нужно разорвать прямую связь i, завернув ее в другой lambda, вызываемый внутри. выражения понимания списка:
Перед:
>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2
После:
>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1
(У меня была и внешняя лямбда-переменная = i, но я решил, что это более ясное решение - я представилy чтобы мы все могли видеть, какая ведьма какая).
Изменить 2019-08-30:
Следуя предложению @josoler, которое также присутствует в ответе @sheridp - значение «петлевой переменной» для понимания списка может быть «встроено» в объект - ключ для доступа к нему в нужное время. Раздел «После» выше делает это, оборачивая его в другой lambdaи вызывая его немедленно с текущим значением i. Другой способ (немного проще для чтения - он не производит эффекта «WAT») - сохранить значение iвнутри partialобъекта, а «внутренний» (исходный) lambdaпринять его в качестве аргумента (передаваемый отpartial объектом в время звонка), т.е.
После 2:
>>> from functools import partial
>>> a = [partial(lambda y, x: y * x, i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
Отлично, но есть еще маленький поворот для вас! Допустим, мы не хотим облегчить чтение кода и передаем фактор по имени (в качестве ключевого аргументаpartial ). Давайте сделаем переименование:
После 2.5:
>>> a = [partial(lambda coef, x: coef * x, coef=i) for i in (1, 2)]
>>> a[0](1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() got multiple values for argument 'coef'
WAT?
>>> a[0]()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'x'
Подождите ... Мы меняем число аргументов на 1 и переходим от «слишком много» к «слишком мало»?
Ну, это не настоящий WAT, когда мы переходим coefк partialтаким образом, становится ключевым словом аргумент, поэтому он должен прийти после того , как позиционнаяx аргумента, например так:
После 3:
>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
Я бы предпочел последнюю версию над вложенной лямбдой, но каждому свою ...
[lambda x: x*x for x in range(10)]быстрее, чем первый, так как он не вызывает функцию внешнего цикла, f повторно.