I
I
Ilkhomjon Matazimov2020-05-20 11:22:07
Python
Ilkhomjon Matazimov, 2020-05-20 11:22:07

The for loop leaves only the last line from the array. Why?

Good day!
I've been asking this question for a long time, but so far no one has answered me. In my telegram bot, the for loop iterates over only the last line from the array. I use the library for telegram bots "Aiogram".

Here is the code:

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)

And so food in the loop takes and leaves only the last line from the array. That is, if you change the order of the lines, and put "Potato" on the last line, then only Potato will come to food, and so on.

Help me please!!!

Answer the question

In order to leave comments, you need to log in

3 answer(s)
K
kamenyuga, 2020-05-20
@kamenyuga

Works exactly as it should. Because Python is a dynamic language. To understand the problem, you need to understand exactly how functions and their arguments work. (1) normal arguments are resolved at function call time, (2) default arguments are resolved at function definition time, (3) non-local variables within a function are resolved at function runtime (look up in external or global namespaces). I can’t figure out what word to choose instead of “resolve”. Here is the third option and it works in the above code, but the second one should work.
Therefore, it is necessary to write not like this:
lambda c: c.data == food
but like this:
lambda c, food=food: c.data == food
Well, a simple example for reproducing such an effect:

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)

A
alternativshik, 2020-05-20
@alternativshik

Yes, as written, and it works.

B
bbkmzzzz, 2020-05-20
@bbkmzzzz

remove all unnecessary and you will see that the loop iterates over everything correctly. The rest is debugging. Add logs\prints and debug debug debug

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

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question