mr_qpdb
@mr_qpdb
⏱ - is not eternal

Цикл for оставляет лишь последнюю строку из массива. Почему?

Доброго времени суток!
Я уже давно задаю этот вопрос, но так до сих пор мне никто не ответил. В моем telegram боте, цикл for перебирает из массива только последнюю строку. Использую библиотеку для telegram ботов "Aiogram".

Вот код:
keyboard = types.InlineKeyboardMarkup()
foods = ['Бургер', 'Картофель', 'Куринные ножки']
for food in foods:
    inline_btns = types.InlineKeyboardButton(food, callback_data=food)
    keyboard.add(inline_btns)

    @dp.callback_query_handler(lambda c: c.data == food) # В food попадает лишь последняя строка из массива.
    async def process_callback(call: types.CallbackQuery):
        await bot.edit_message_text(text=f"Нажата кнопка {food}", chat_id=call.message.chat.id,
                                    message_id=call.message.message_id)
await message.answer("Выберите блюдо:", reply_markup=keyboard)

И так food в цикле берет и оставляет только лишь последнюю строку из массива. То-есть, если изменить порядок строк, и на последнюю строку поставить "Картофель", то в food придет только Картофель и т.д.

Помогите, пожалуйста!!!
  • Вопрос задан
  • 667 просмотров
Пригласить эксперта
Ответы на вопрос 3
@kamenyuga
Работает ровно так, как и должно. Ибо Пайтон - язык динамический. Чтобы разобраться в проблеме, надо понимать как именно работают функции и их аргументы. (1) обычные аргументы резолвятся во время вызова функции, (2) дефолтные аргументы резолвятся во время определения функции, (3) нелокальные переменные внутри функции резолвятся во время работы функции (ищутся во внешнем или глобальном пространствах имен). Не соображу, какое слово подобрать вместо "резолвятся". Вот третий вариант и работает в приведенном коде, а должен работать второй.

Следовательно, надо писать не так:
lambda c: c.data == food

а вот так:
lambda c, food=food: c.data == food

Ну, и простой пример для воспроизведения такого эффекта:

from typing import List


def f_1(lst: List) -> None:

    funcs = list()
    for val in lst:
        funcs.append(lambda: val)
    # цикл закончился, val имеет значение последнего элемента из списка

    for func in funcs:
        print(func())


def f_2(lst: List) -> None:

    funcs = list()
    for val in lst:
        funcs.append(lambda val=val: val)

    for func in funcs:
        print(func())


if __name__ == '__main__':

    data = ['apple', 'tomato', 'potato']

    print("\nf_1:")
    f_1(data)

    print("\nf_2:")
    f_2(data)
Ответ написан
Комментировать
alternativshik
@alternativshik
Да как написал, так и работает оно.
Ответ написан
@bbkmzzzz
уберите все лишнее и увидите, что цикл перебирает все корректно. Остальное отладка. Добавляйте логи\принты и дебаг дебаг дебаг

foods = ['Бургер', 'Картофель', 'Куринные ножки']
for food in foods:
  print(food)
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы