Почему в качестве параметра функции используется только последний индекс?

Я использую библиотеку Pystray для создания массива кнопок, каждая из которых должна передавать индекс своего устройства в функцию, которая устанавливает это устройство по умолчанию. Однако, по непонятной причине, при нажатии на любую из кнопок всегда выбирается только последний индекс. У меня есть три микрофона с индексами 4, 5 и 6, и независимо от того, на какую кнопку я нажимаю, выбирается только индекс 6. Как я могу решить эту проблему?
  • 30 ноября 2024 г. 15:23
Ответы на вопрос 2
Это распространенная проблема в Python, связанная с замыканием (closure) в циклах. Когда вы используете переменную цикла в функции, эта переменная сохраняет последнее значение после завершения цикла.

В вашем случае, если вы передаете индекс в обработчик события кнопки, который создается в цикле, все кнопки будут ссылаться на одну и ту же переменную, которая в момент нажатия кнопки будет иметь последнее значение.

Чтобы решить эту проблему, вы можете использовать аргумент по умолчанию в функции, чтобы зафиксировать текущее значение переменной:

```python
import pystray
from pystray import MenuItem as item

def set_device(index):
    print(f'Selected device index: {index}')

# Список индексов ваших устройств
device_indices = [4, 5, 6]

# Создайте список кнопок для вашего меню
menu_items = [
    item(f'Device {index}', lambda _, idx=index: set_device(idx)) for index in device_indices
]

# Создайте и запустите иконку
icon = pystray.Icon('test_icon', menu=pystray.Menu(*menu_items))
icon.run()
```

В этом примере мы используем `lambda` с параметром `idx=index`. Это создает новую переменную `idx` для каждой кнопки, которая сохраняет текущее значение `index` на момент создания кнопки. Таким образом, даже если цикл завершится, каждая функция-обработчик будет иметь доступ к своему собственному значению индекса.
Поздравляю, ты попался на лямбду. 
Лямбды сохраняют ссылки на переменные, а не их значения.
lambda _: set_default_microphone(index) сохранит ссылку на index, а потому все лямбды будут видеть одно и то же значение index, которое было установлено последним.
Это можно обойти, сохранив ссылку при объявлении лямбды. Самый простой способ - засунуть сохраняемое значение в значение по умолчанию для неиспользуемого параметра. Значения по умолчанию для параметров вычисляются один раз при объявлении функции, в т.ч. лямбды.
Например, так:
lambda _, *, ind=index: set_default_microphone(ind)

Ну или даже так:
lambda _, *, index=index: set_default_microphone(index)
Похожие вопросы