Це питання стосується дуже смердючої частини «знаменитого» та «очевидного» синтаксису Python - що має перевагу, лямбда чи розуміння списку.
Я не думаю, що метою ОП було генерувати список квадратів від 0 до 9. Якби це було так, ми могли б дати ще більше рішень:
squares = []
for x in range(10): squares.append(x*x)
- це хороший ol 'спосіб імперативного синтаксису.
Але справа не в цьому. Справа в тому, що 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змінна. Це не має нічого спільного з xв lambda(це затьмарено лексично!). Отримаєте?
Я покидаю дискусію, чому пріоритет синтаксису не такий, що це все означало:
[lambda x: (x*x for x in range(10))]
що може бути: [[0, 1, 4, ..., 81]]або [(0, 1, 4, ..., 81)], або що я вважаю найбільш логічним , це буде list1 елемент - generatorповертає значення. Це просто не так, мова не працює так.
АЛЕ що, якщо ...
Що робити, якщо ви НЕ затьмарюєте forзмінну, а використовуєте її у своїх lambdas?
Ну, тоді трапиться. Подивись на це:
[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-агностиків цього достатньо. Стільки для питання співбесіди.
Тепер, як скласти 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'
ВАТ?
>>> a[0]()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'x'
Зачекайте ... Ми змінюємо кількість аргументів на 1 і переходимо від "занадто багато" до "занадто мало"?
Ну, це не справжній ВАТ, коли ми переходимо 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 повторно.