[python] Привязка переменных в list displays
От: Roman Odaisky Украина  
Дата: 23.11.09 21:55
Оценка:
Требуется создать несколько лямбда-функций внутри выражения list display (оно же list comprehension). Это нужно для DeferredList в Twisted. Пишу примерно так:
return defer.DeferredList([
   make_deferred_somehow(j).addCallback(
       lambda x: perform_postprocessing(x, i) # это i должно быть каждый раз разным
   )
   for i, j in get_some_data()
])

Однако получается невесть что — все лямбда-выражения ссылаются на одно и то же значение i. Минимальный пример:
>>> constant = lambda x: lambda: x
>>> [f() for f in [constant(i) for i in range(5)]]
[0, 1, 2, 3, 4] # ожидаемое поведение
>>> [f() for f in [(lambda: i) for i in range(5)]]
[4, 4, 4, 4, 4] # почему все λ привязались к одной и той же i?!

И даже так:
>>> fs = ((lambda: i) for i in range(5))
>>> f0 = next(fs)
>>> f1 = next(fs)
>>> f0()
1
>>> f1()
1
>>> f2 = next(fs)
>>> f0()
2
>>> f1()
2

Пока нашел только один workaround:
>>> [f() for f in [(lambda i=i: i) for i in range(5)]]
[0, 1, 2, 3, 4]

А как сделать по-человечески?
До последнего не верил в пирамиду Лебедева.
Re: [python] Привязка переменных в list displays
От: Daevaorn Россия  
Дата: 23.11.09 22:16
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>А как сделать по-человечески?


По сути это и есть по-человечески. Да, у питона такая особенность — переменные в замыканиях биндятся по имени к оным в родительском фрейме.
Re: [python] Привязка переменных в list displays
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 24.11.09 07:40
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>А как сделать по-человечески?


from functools import *

print ([f() for f in [partial(lambda x : x, i) for i in range(5)]])


http://docs.python.org/library/functools.html#functools.partial

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[2]: [python] Привязка переменных в list displays
От: Roman Odaisky Украина  
Дата: 24.11.09 07:59
Оценка:
Здравствуйте, Daevaorn, Вы писали:

D>Да, у питона такая особенность — переменные в замыканиях биндятся по имени к оным в родительском фрейме.


А где можно почитать об этом?
До последнего не верил в пирамиду Лебедева.
Re: [python] Привязка переменных в list displays
От: Mr.Cat  
Дата: 25.11.09 06:07
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:
RO>А как сделать по-человечески?
А чем тебе не нравится твой вариант с constant? По мне так вполне нормальный.
Не заботится Гвидо о любителях функциональщины. Похоже, компрехеншн переписывается в for, а у for, похоже, переменная цикла создается один раз и потом просто мутируется на каждой итерации. Вот все замыкания и видят эти мутации. В твоем воркэраунде с constant — переменная цикла передается в лямбду в качестве параметра, то бишь копируется — а тебе этого и надо. Что интересно, вокруг подобной ситуации в шарпе неслабый шум поднялся (ключевая фраза — "closing over loop variable").
Re[2]: [python] Привязка переменных в list displays
От: Roman Odaisky Украина  
Дата: 25.11.09 08:54
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>А чем тебе не нравится твой вариант с constant? По мне так вполне нормальный.


Там функции посложнее и каждый раз разные.

MC>Что интересно, вокруг подобной ситуации в шарпе неслабый шум поднялся


Я помню — http://rsdn.ru/forum/dotnet/2760035.1.aspx
Автор: nikov
Дата: 09.12.07
.

Вообще, насколько я понял, еще ни одному языку не удалось избавиться от указателей, не наставив подобных граблей. То же самое касается типизации...
До последнего не верил в пирамиду Лебедева.
Re[3]: [python] Привязка переменных в list displays
От: Mr.Cat  
Дата: 25.11.09 10:05
Оценка: 4 (1)
Здравствуйте, Roman Odaisky, Вы писали:
MC>>А чем тебе не нравится твой вариант с constant? По мне так вполне нормальный.
RO>Там функции посложнее и каждый раз разные.
Т.е. нужно решение для общего случая: лямбда захватывает несколько мутабельных переменных, надо обеспечить, чтобы их значения были зафиксированы в состоянии на момент создания лямбды и при этом не хочется переписывать сами лямбды? Просто захватываемые переменные можно было бы явно вынести в параметры например, вместо lambda x: f(i, j, k) писать что-то типа (lambda i, j, k: lambda x: f(i, j, k))(i, j, k).

В более другом языке я бы написал макрос, разворачивающийся в вот такую копипасту: (lambda <тут параметры>: <обернутая лямба идет сюда>)(<и тут параметры>). С метапрограмированием на питоне я особо не знаком, но, быть может, найдется способ как-то похачить захваченное лямбдой окружение.

RO>Вообще, насколько я понял, еще ни одному языку не удалось избавиться от указателей, не наставив подобных граблей. То же самое касается типизации...

Мне кажется, дело не в избавлении от указателей, а во внесении функциональщины в язык с мутабельными переменными и мутабельными структурами данных. В в принципе, во всяких scheme тоже можно на подобное напороться — но там мутабельность — это скорее исключение и используется по делу.
Re[2]: [python] Привязка переменных в list displays
От: neFormal Россия  
Дата: 30.11.09 12:17
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>А чем тебе не нравится твой вариант с constant? По мне так вполне нормальный.

MC>Не заботится Гвидо о любителях функциональщины. Похоже, компрехеншн переписывается в for

иное было б странно, т.к. в LC как раз for и пишется..

можно было бы обойти это, выделив для переменной в цикле свой scope и по завершению итерации делать del i .. типа такого
>>> for i in xrange(10):
...  lst.append(lambda: i)
...  del i
... 
>>> lst
[<function <lambda> at 0xb7739a3c>, <function <lambda> at 0xb7739ae4>, <function <lambda> at 0xb773910c>, <function <lambda> at 0xb7739a74>, <function <lambda> at 0xb7739b1c>, <function <lambda> at 0xb7739b54>, <function <lambda> at 0xb7739b8c>, <function <lambda> at 0xb7739bc4>, <function <lambda> at 0xb7739bfc>, <function <lambda> at 0xb7739c34>]
>>> for f in lst: f()
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in <lambda>
NameError: global name 'i' is not defined


правда, тут я не понимаю логики, почему i удалено оказалось.. имхо ссылка на i должна оставаться в лямбде..
...coding for chaos...
Re: [python] Привязка переменных в list displays
От: neFormal Россия  
Дата: 30.11.09 14:36
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>
RO>return defer.DeferredList([
RO>   make_deferred_somehow(j).addCallback(
RO>       lambda x: perform_postprocessing(x, i) # это i должно быть каждый раз разным
RO>   )
RO>   for i, j in get_some_data()
RO>])
RO>

RO>А как сделать по-человечески?

тут подсказали заюзать генераторы.. "простейший" пример начинает выглядеть так:
>>> d = {1: 1, 2: 2, 3: 3}
>>> [(p[1],p[0](123)) for p in ((lambda x: (x,i),j) for i,j in d.items()) ]
[(1, (123, 1)), (2, (123, 2)), (3, (123, 3))]

собственно, главное отличие в том, что вместо вложенного списка создаётся вложенный генератор кортежей вида (лямбда-j)..
в твоём коде это будет выглядь как то так (относительно предыдущего примера только лямбда и x поменялись местами):
return defer.DeferredList([
   make_deferred_somehow(gen[0]).addCallback(gen[1])
   for gen in ((j, lambda x: perform_postprocessing(x, i)) for i,j in get_some_data())
])
...coding for chaos...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.