Как можно создать элементы в цикле в Python?

Добрый день! У меня возникли трудности с обработкой JSON-данных. <br/><br/> Я получаю следующий JSON в ответе на запрос: <br/> 
<pre><code class="php">{"1":{"title":"\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u0430\u043d\u0430\u043b","stream":"http:\/\/neformatnoe.ru:8777\/live"},"2":{"title":"\u041a\u0430\u043d\u0430\u043b \u041b\u0438\u0440\u0438\u043a\u0430","stream":"http:\/\/neformatnoe.ru:8777\/lirika"},"3":{"title":"\u041a\u0430\u043d\u0430\u043b \u0425\u0438\u043f\u0425\u043e\u043f","stream":"http:\/\/neformatnoe.ru:8777\/hiphop"},"4":{"title":"\u041a\u0430\u043d\u0430\u043b \u041a\u043b\u0443\u0431\u043d\u044f\u043a","stream":"http:\/\/neformatnoe.ru:8777\/club"},"5":{"title":"\u041a\u0430\u043d\u0430\u043b \u0416\u0435\u0441\u0442\u044c","stream":"http:\/\/neformatnoe.ru:8777\/hard"}}</code></pre> <br/><br/> Затем, используя цикл, создаю кнопки: <br/><br/> 
<pre><code class="python">list_str = response.json()
i=1
for val in list_str.values():
    button = customtkinter.CTkButton(window, text=f"{val['title']}", command=lambda:radio(val['stream']))
    button.grid(row=i, column=1, sticky="n", pady=10)
    label = customtkinter.CTkLabel(window, text=f"{val['stream']}")
    label.grid(row=i, column=2, sticky="n", pady=10)
    
    print(val['title']) #принтую чисто для себя для контроля
    i=i+1 #итерациями цикла пытался добавлять индекс к button_X</code></pre> <br/> Однако у меня возникла проблема: при нажатии на любую кнопку передаётся значение последней кнопки из цикла. Я понимаю, что это связано с тем, что все объекты кнопок имеют одинаковое имя - @button. Изучив документацию по Python, я пришёл к выводу, что переменным нельзя присваивать имена внутри цикла, а только через прямое объявление в коде. <br/><br/> Я понимаю, что для пяти кнопок можно было бы сделать всё вручную, но как мне правильно решить эту проблему для динамически создаваемых кнопок?
  • 16 марта 2025 г. 15:15
Ответы на вопрос 2
Ваша проблема связана с тем, что вы используете `lambda`, чтобы передать значение в команду кнопки. Когда вы создаете анонимную функцию с использованием `lambda`, она захватывает переменные по ссылке, а не по значению. Таким образом, к моменту, когда вы нажимаете на любую из ваших кнопок, значение переменной `val` уже будет равно последнему элементу.

Чтобы это исправить, можно использовать аргументы в `lambda`, чтобы передать текущее значение `val` в каждый `lambda` при его создании. Вот как это можно сделать:

```python
list_str = response.json()
i = 1
for val in list_str.values():
    # Используем аргумент в lambda для захвата текущего значения `val`
    button = customtkinter.CTkButton(window, text=f"{val['title']}", command=lambda stream=val['stream']: radio(stream))
    button.grid(row=i, column=1, sticky="n", pady=10)
    
    label = customtkinter.CTkLabel(window, text=f"{val['stream']}")
    label.grid(row=i, column=2, sticky="n", pady=10)
    
    print(val['title'])  # принтую чисто для себя для контроля
    i += 1  # итерациями цикла пытался добавлять индекс к button_X
```

В этом фрагменте кода важный момент — в `lambda` добавлен аргумент `stream=val['stream']`. Это означает, что текущее значение `val['stream']` будет передано в `lambda`, и при нажатии на кнопку использовано именно это значение, а не последнее значение, оставшееся в переменной `val`. 

Такая техника передачи параметров в `lambda` решает вашу проблему и позволит каждой кнопке передавать правильный поток нажатия.
command=lambda v=val: radio(v['stream'])
Похожие вопросы