Как создать несколько add_callback в торнадо?
Я пытаюсь добавить несколько обратных вызовов в основной цикл торнадо. Но когда я запускаю этот код:
def task(num):
print 'task %s' % num
if __name__ == '__main__':
for i in range(1,5):
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(num=i))
tornado.ioloop.IOLoop.instance().start()
Я получаю вывод 5 раз: 'задача 5', а не задача 1.. задача 5. Когда я изменяю main следующим образом:
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(1))
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(2))
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(3))
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(4))
все работает нормально (я получаю task1-task5 на выходе). Что я делаю не так в первом случае?
2 ответа
Может быть, есть такая же проблема, как в JS? посмотрите на этот ответ: закрытие JavaScript внутри циклов - простой практический пример
"проблема в том, что переменная i в каждой из ваших анонимных функций связана с одной и той же переменной вне функции".
попробуйте простой тест:
def task(num):
print 'task %s' % num
def create_task(num):
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(num))
if __name__ == '__main__':
for i in range(1,5):
create_task(i)
tornado.ioloop.IOLoop.instance().start()
Оберните вашу лямбду в функцию functools.partial(), которая решает "проблему с указателем"
IOLoop.instance().add_callback(callback=functools.partial(task, i*10))
ТЛ; др;
Вот пример с "ошибкой указателя":
def task(num):
print(num)
for i in range(1, 6):
print ("poop {0}".format(i))
IOLoop.instance().add_callback(callback=lambda: task(i))
IOLoop.instance().start()
в приведенном выше примере кода i
будет указатель памяти на значение range()
в настоящее время повторяется. Он меняется с 1 на 2, затем на 3, 4, 5.
Асинхронная функция Tornado add_callback() возвращает управление основной программе, немедленно делая все 5 вызовов task()
Функция выполняется практически одновременно. И в этот момент i
уже получил значение 5
,
Чтобы решить эту проблему быстро используйте funtools.partial
работай вместо лямбды и исследуй разницу;)
Вот пример без ошибок:
IOLoop.instance().add_callback(callback=functools.partial(task, i*10))