LINUX.ORG.RU

Неправильное поведение лямбда-функций

 , ,


0

2

Есть у меня вот такой код:

funcs = []

for i in (0,1):
 funcs.append(lambda: i == 1)

print funcs[0], funcs[0]()
print funcs[1], funcs[1]()

Выдаёт он мне:

<function <lambda> at 0x0000000001D72518> 1
<function <lambda> at 0x0000000001FDDDD8> 1

Как бы мне сделать так, чтобы первая функция выдавала 0, а вторая - 1? Где здесь собака зарыта?


Поведение правильное, читай ссылки данные выше, чтобы понять, как оно работает под низом.

anonymous ()
Ответ на: комментарий от true_admin

Злые вы все... человек просто спросил, а сразу ссылками кидаться.

for i in (0,1): funcs.append(lambda i=i: i == 1)
AIv ★★★★★ ()
Ответ на: комментарий от true_admin

Как и написано по твоей ссылке, lambda closes over variables, not values.

tailgunner ★★★★★ ()
Ответ на: комментарий от true_admin

По питонячьтему же! Это че,

funcs = []

for i in (0,1):
 funcs.append(lambda: i == 1)

del i

print funcs[0], funcs[0]()
print funcs[1], funcs[1]()

вот где самый баттхерт то... вообще при импорт * из модуля обычно стока мусора вываливается, типа счетчиков циклов.

AIv ★★★★★ ()
Ответ на: комментарий от AIv

вообще при импорт * из модуля обычно стока мусора вываливается, типа счетчиков циклов.

Питон здесь не при делах - это кривые руки или пустые головы.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Питон здесь не при делах

Можно было добавить (если нету) возможность явного указания списка экспорта.

anonymous ()
Ответ на: комментарий от tailgunner

Питон здесь при делах - он по умолчанию позволяет так косячить. Казалось бы, что счетчику цикла делать за пределами цикла... это то ладно, а вот что счетчику генератора списка делать за пределами списка? Хотя вроде в тройке поправили

AIv ★★★★★ ()
Ответ на: комментарий от AIv

что счетчику цикла делать за пределами цикла...

А он определен как имеющий отдельное пространство имен? Если нет, то убирать после - костыль.

anonymous ()
Ответ на: комментарий от anonymous

Можно было добавить (если нету) возможность явного указания списка экспорта.

Она есть. Более того, * считается плохим стилем.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от AIv

Питон здесь при делах - он по умолчанию позволяет так косячить

Как «так» - писать в глобальном скопе, а потом импортировать модуль? Ты точно уверен, что это косяк Питона?

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

На 50% - именно питона. Создатели сделали как проще, пользователи тоже делают как проще, а потом на лоре вот такие посты появляются...

AIv ★★★★★ ()
Ответ на: комментарий от tailgunner

Она есть. Более того, * считается плохим стилем.

В смысле в модуле? (Поэтому «экспорта» написал.)

Относительно нотации импорта - все понятно.

anonymous ()
Ответ на: комментарий от AIv

Создатели сделали как проще

Они сделали удобно и дали программисту выбор.

потом на лоре вот такие посты появляются...

А предложи другую семантику.

tailgunner ★★★★★ ()
Ответ на: комментарий от AIv

Не выпускать счетчики за пределы цикла?;-)

Иии... что?

Alv> потом на лоре вот такие посты появляются...

«такой пост» был связа с лямбдой в цикле, как это исправит «невыпускание счетчика»?

tailgunner ★★★★★ ()
Ответ на: комментарий от AIv

Не выпускать счетчики за пределы цикла?;-)

Замыкание оно на то и замыкание чтоб захватывать переменные в лексическом контексте. Другое дело что оно в питоне довольно странное — если я правильно помню из замыкания переменную поменять нельзя

maxcom ★★★★★ ()
Ответ на: комментарий от tailgunner

Я знал, что ты это скажешь. Но если бы счетчик за пределы цикла не выходил - пост был бы несколько другой;-)

AIv ★★★★★ ()
Ответ на: комментарий от maxcom

Замыкание оно на то и замыкание чтоб захватывать переменные в лексическом контексте.

Захватывать в смысле запоминать текущее значение же?

Другое дело что оно в питоне довольно странное — если я правильно помню из замыкания переменную поменять нельзя

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

AIv ★★★★★ ()
Ответ на: комментарий от AIv

Захватывать всмысле захватывать, а не всмысле копировать :-)

maxcom ★★★★★ ()
Ответ на: комментарий от AIv

там банально обращение к глобальной переменной, оставшейся в виде мусора после работы цикла.

Если бы эта переменная была локальной, а фрагмент кода был телом функции, это ничего бы не изменило. Глобальность не причем.

tailgunner ★★★★★ ()
Ответ на: комментарий от anonymous

В смысле в модуле? (Поэтому «экспорта» написал.)

Поэтому тебе и ответили, что есть. RTFM.

anonymous ()

Тред полон 5-ти и 4-х звездочников, не понимающих сути замыкания, сщитающих такое поведение WTFаком и винящих глобальный скоуп. Это печально.

anonymous ()
Ответ на: комментарий от anonymous

В смысле в модуле?

Да, но только в Ъ-модуле (с __init__.py): нужно указать экспортируемые имена в списке __all__.

tailgunner ★★★★★ ()
Ответ на: комментарий от AIv

Захватывать в смысле запоминать текущее значение же?

ага, а если переменная — ссылка на объект, чё создавать копию объекта с текущим состоянием, чтоли? В императивщине — это дорого и грозит сайдэффектами.

Bad_ptr ★★★★ ()
Последнее исправление: Bad_ptr (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Изменилось бы только содержание поля func_closure, но ТС бы этого не узнал. Хотя это приводит к интересным эффектам, напр. переменную висящую на замыкании нельзя удалить.

AIv ★★★★★ ()
Ответ на: комментарий от tailgunner

Они сделали удобно и дали программисту выбор.

да? а как мне тогда удобно сделать выбор «локальная переменная, видимая только в теле цикла»?

вот скажем в перле правильный выбор делается печатью двух букв my:

#!/usr/bin/perl -W

$i=99;

print "a: i=$i\n";

for(my $i=0; $i<3; ++$i) {
  print "b: i=$i\n";
}

print "c: i=$i\n";

for(local $i=0; $i<3; ++$i) {
  print "d: i=$i\n";
}

print "e: i=$i\n";

$ ./perl-my.pl 
a: i=99
b: i=0
b: i=1
b: i=2
c: i=99
d: i=0
d: i=1
d: i=2
e: i=3
$ 

А предложи другую семантику.

так очевидно же — переменная определена только внутри цикла, а чтобы выдать ее наружу, испосльзовать спец. средства, скажем finally, аналогичное тому else, что есть в питоне (к слову, else тоже говно)

for i in range(1, 100500):
  if very_bad(i):
    break
  sum+=i
finally:
  print "sum=", sum, " up to i=", i
www_linux_org_ru ★★★★★ ()
Последнее исправление: www_linux_org_ru (всего исправлений: 2)
Ответ на: комментарий от tailgunner

а возвращаясь к проблеме ТС, можно предложить такую семантику: если замыкание захватывает переменную, локальную для итерации цикла (т.е. локальную для цикла и изменяющуюся в следующей итерации), то эта переменная в замыкание копируется

это немного необычная для людей семантика, но у нее есть определенные корни (в частности то, что замыкание может пережить цикл, но не только это), и поэтому ее стоило бы обдумать

www_linux_org_ru ★★★★★ ()
Последнее исправление: www_linux_org_ru (всего исправлений: 4)
Ответ на: комментарий от anonymous

Тред полон 5-ти и 4-х звездочников, не понимающих сути замыкания, сщитающих такое поведение WTFаком и винящих глобальный скоуп. Это печально.

нелокальный скоуп — это часть питоновского бардака, мутящего и так мутную водичку вокруг питона

считать ли это причиной — более сложный вопрос

www_linux_org_ru ★★★★★ ()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от www_linux_org_ru

если замыкание захватывает переменную, локальную для итерации цикла (т.е. локальную для цикла и изменяющуюся в следующей итерации), то эта переменная в замыкание копируется

это немного необычная для людей семантика

...в которой куски кода:

l, i = 0, []
for i in range(2):
  l.append(lambda: i)
l = []
for i in range(2):
  l.append(lambda: i)

имеют разную семантику. Зашибись.

нелокальный скоуп — это часть питоновского бардака

Бардак - он, как обычно, в головах. В примере ТС нет никакого «нелокального скоупа».

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от SAA

Кроме «не осилил», какие? =)

Я посмотрел как выглядит код простеньких вебсайтов на хаскеле. По-моему, это сильно проигрывает питону в читабельности.

true_admin ★★★★★ ()
Ответ на: комментарий от true_admin

Главный вопрос это «а что можно писать на хаскеле?»

С точки зрения пользователя:

pandoc же ^_^ - вполне удобная вещь, из того чем еще пользуюсь: gitit - редко, shelltestrunner (кстати рекомендую - удобная вещь), обвязка вокруг Criterion - сравнивать консольные программы. Да и браузер - перепиленный и недопиленный hbro.

А на питоне: gozebor (раньше), setconf (очень редко), pactools - редко, smem - редко, hg (считать?), uimge - но что-то в последнее врямя отвалился.

Особого преобладания не вижу.

anonymous ()
Ответ на: комментарий от tailgunner

Например, какой?

Пакетный менеджер сойдет за системный? Вроде есть дистрибутив, но навскидку не назову какой.

anonymous ()
Ответ на: комментарий от tailgunner

Можно писать системный софт? На хаскелле? Например, какой?

Если не путаю, облако selectel переписали с питона на хаскель. Точно, вот: http://habrahabr.ru/company/selectel/blog/135858/

Я пользовался их услугами, оно работало :)

true_admin ★★★★★ ()
Ответ на: комментарий от tailgunner

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

SAA ★★★ ()
Ответ на: комментарий от anonymous

Я думаю мы говорим о более сложных сайтах, чем статичные.

SAA ★★★ ()
Ответ на: комментарий от tailgunner

Возможно ты и прав, и я зря сказал про системный софт. В этом плане ocaml лучше. На нем вполне успешно написан xapi и большая часть утилит/систем в XCP.

SAA ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.