Как создать несколько 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))
Другие вопросы по тегам