python 3.1 не разобрался с lambda

yroslavasako

Есть простой вопрос для тех, кто питон практикует, но для новичка, только прочитавшего туториал он сложен и не понятен.
Почему код

def mapper(x):
return x*x

def main:
lst = range(5)
print ([mapper(x) for x in lst])
closures = [ (lambda: mapper(x for x in lst ]
print ([x for x in closures ])

if __name__ == '__main__':
main

выдаёт
[0, 1, 4, 9, 16]
[16, 16, 16, 16, 16]
Оба варианта казалось бы должны давать одинаковый результат. Чего я не учёл? Или в питоне функции тоже не первоклассные объекты, а, скажем так, полтораклассные?

Helga87

Какая у тебя версия python?

yroslavasako

3.1

Dmitriy82

Не уверен, имеет ли это отношение к -классности функций, но переменные из внешних неглобальных областей в кложуры попадают не по значению, а по ссылке. А х - это одна переменная, которая последовательно пробегает разные значения.

Helga87

3.1
У меня на 2.x давала runtime error, поскольку внутрь mapper-а шла сама lambda (похоже на убогую реализацию list comprehensions)

yroslavasako

А х - это одна переменная, которая последовательно пробегает разные значения.
ясно, а как сделать то, что я хочу? (то бишь передать по значению тем или иным путём). Какой путь pythonic-way?

Lexa111


closures = map(lambda x: lambda: mapper(x lst)

yroslavasako

то есть map не эквивалентен list comprehension. Но мне последний больше по нраву, он удобнее и выглядит понятнее.
тогда уж можно заюзать другую стороннюю тулзу:

closures = [ partial(mapper,x) for x in lst ]

update:
(похоже на убогую реализацию list comprehensions)
а видимо ты прав. Причём убогость не искоренили полностью даже в третей версии

yroslavasako

всё, нашёл решение. Спасибо предыдущему оратору.

lamlam = [ (lambda x: lambda: mapper(xx) for x in lst ]

в очередной раз убеждаюсь, что понятие "интуитивно понятный язык" некорректно. Сколько таких не анонсировалось, а везде свои загоны

pitrik2

дачот ваще жесть какаято
неужель попроще нильзя?

yroslavasako

дачот ваще жесть какаято
чтобы выглядело проще можно использовать функцию partial из библиотеки functools. Она создаст объект, в котором будет хранить ссылку на оригинальную функцию и список аргументов.

Andbar

В JavaScript-е, кстати, подобная проблема тоже есть и, вроде, решается она аналогичным образом...
В общем-то, смысл в том, что обращение по ссылке происходит в момент выполнения лямбды, а не при создании. А вот в C++0x-шных лямбдах, кстати, способ доступа к внешним переменным прописываются в заголовке.

tipnote

в очередной раз убеждаюсь, что понятие "интуитивно понятный язык" некорректно. Сколько таких не анонсировалось, а везде свои загоны
а то
читай и наслаждайся бояном:
http://wiki.python.org/moin/PythonWarts

bleyman

Ойёёё, там как-то по ходу всё совсем криво.
Если заменить второй принт на
print ([y for y in closures])
, то в 2.6.4 тоже всё начинает работать. Ну то есть печатать 16 много раз. Видимо, у них утекают переменные из компрехенжена во внешний scope. Поэтому все вхождения слова x на самом деле реферят одну и ту же переменную, поэтому происходит фигня. В 3.х, видимо, пофиксили это, но не до конца: теперь переменная компрехенжена живёт в скоупе компрехенжена, но по-прежнему одна на весь скоуп.
А разгадка одна — в Питоне переменные объявляются присваиванием, поэтому scopes ущербны донельзя.
В сишарпе, кстати, та же проблема у обычного foreach, и за это их совсем простить нельзя. В смысле что `foreach (var v in x) embedded-statement` согласно спецификации раскрывается в

{
E e = Cx.GetEnumerator;
try {
V v;
while (e.MoveNext {
v = (VT)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}

Если бы они написали V v = (VT)e.Current;, объявив переменную прямо во внутреннем скоупе, всё было бы отлично, а так она получается общая, соответственно все захватывающие её лямбды захватывают одну и ту же. Причём они об этом знают, но не исправляют, потому что "breaking change" и бла бла бла, причём я даже не уверен, что они хотя бы в четвёртом шарпе это сделают ошибкой (потому что специально это НИКТО НИКОГДА не напишет чтобы исправить в каком-нибудь пятом. Причём в LINQ всё работает правильно!

psihodog

Видимо, у них утекают переменные из компрехенжена во внешний scope. Поэтому все вхождения слова x на самом деле реферят одну и ту же переменную, поэтому происходит фигня. В 3.х, видимо, пофиксили это
ну, это известная весчь
http://docs.python.org/dev/3.0/whatsnew/3.0.html#changed-syn...
(предпоследний абзац)
но не до конца: теперь переменная компрехенжена живёт в скоупе компрехенжена, но по-прежнему одна на весь скоуп.
имхо, вполне ожидаемое поведение.
создавать свой словарь на каждую итерацию цикла — это вообще разврат, итак питоньи оптимизаторы плачут кровавыми слезами

pitrik2

print ([y for y in closures])
у меня в 2.5.4 эта конструкция вообще не работает :(


>>> items = [0, 1, 2]

>>> it = [(lambda : x*x) for x in items]

>>> it
[<function <lambda> at 0x5f1b18>, <function <lambda> at 0x5f1de8>, <function <lambda> at 0x5f1e60>]

>>> [x for x in it]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
TypeError: unsupported operand type(s) for *: 'function' and 'function'

причем в 2.5.4 вот такое работает

>>> it[0]
4

а в 2.5.1 даж это не работает

>>> it[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
TypeError: unsupported operand type(s) for *: 'function' and 'function'

psihodog

> у меня в 2.5.4 эта конструкция вообще не работает
сказано же, заменить в последнем случае x на y =)
>>> items = [0, 1, 2]
>>> it = [(lambda : x*x) for x in items]
>>> [y for y in it]
[4, 4, 4]

> а в 2.5.1 даж это не работает
не верю!
напиши в _чистом_ интерпретаторе этот код (без [x for x in it]) и всё будет в порядке

pitrik2

не верю!
о блин
когда с иксами пишешь то оно каким-то макаром ломает массив и после этого всё перестает работать

>>> items = [0, 1, 2]
>>> it = [(lambda : x*x) for x in items]
>>> [y for y in it]
[4, 4, 4]
>>> it[0]
4

>>> [x for x in it]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
TypeError: unsupported operand type(s) for *: 'function' and 'function'
>>> [y for y in it]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
TypeError: unsupported operand type(s) for *: 'function' and 'function'
>>> it[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
TypeError: unsupported operand type(s) for *: 'function' and 'function'

psihodog

не массив, а текущий словарь с "переменными"
он в x записывает функцию, после чего всё "ломается":
In [1]: items = [0, 1, 2]

In [2]: it = [(lambda : x*x) for x in items]

In [3]: type(x)
Out[3]: <type 'int'>

In [4]: [x for x in it]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
C:\Users\vj\<ipython console> in <module>
C:\Users\vj\<ipython console> in <lambda>
TypeError: unsupported operand type(s) for *: 'function' and 'function'

In [5]: type(x)
Out[5]: <type 'function'>
Оставить комментарий
Имя или ник:
Комментарий: