Лямбды обращаются к i
, который удерживается в закрытом состоянии, поэтому все они ссылаются на одно и то же значение (значение i
в функции parse
при вызове лямбд).Более простая реконструкция этого явления:
>>> def do(x):
... for i in range(x):
... yield lambda: i
...
>>> delayed = list(do(3))
>>> for d in delayed:
... print d()
...
2
2
2
Вы можете видеть, что все i
в лямбдах связаны со значением i
в функции do
.Они будут возвращать любое значение, которое оно имеет в настоящее время, и python будет поддерживать эту область действия до тех пор, пока любая из лямбд жива, чтобы сохранить значение для нее.Это то, что называется замыканием.
Простой, но уродливый обходной путь:
>>> def do(x):
... for i in range(x):
... yield lambda i=i: i
...
>>> delayed = list(do(3))
>>> for d in delayed:
... print d()
...
0
1
2
Это работает, потому что в цикле значение current i
связан с параметром i
лямбды.В качестве альтернативы (и, возможно, немного яснее) lambda r, x=i: (r, x)
.Важная часть заключается в том, что, присваивая вне тела лямбды (которое выполняется только позже), вы привязываете переменную к current значению i
вместозначение, которое он принимает в конце цикла.Это делает так, чтобы лямбды не были закрыты в течение i
и каждый мог иметь свое значение.
Так что все, что вам нужно сделать, это изменить строку
yield Request(link, callback=lambda r:self.parse2(r, i))
на
yield Request(link, callback=lambda r, i=i:self.parse2(r, i))
и вы черри.