Как исправить текущее значение переменной из родительской области в лямбде - PullRequest
0 голосов
/ 15 марта 2020

Мне нужно создать пакет обратных вызовов во время выполнения, которые используют значения из словаря (одна пара ключ-значение для каждого обратного вызова). После того, как l oop превысит dict, оно теряется, но обратные вызовы должны сохранять правильные значения. (Это напоминает мне об объемах и замыканиях в JavaScript)

Простой python скрипт для воспроизведения моей проблемы:

cmds = list()

def main():
    values = {'label1':'value1', 'label2':'value2'}

    for l,v in values.items():
        add_command(command = lambda: apply_new_item(l,v))


def apply_new_item(label, value):
    print('{}: {}'.format(label, value))


def add_command(command):
    cmds.append(command)


def call_actions():
    for command in cmds:
        command()


# Create callbacks
main()
# Check callbacks
call_actions()

Для случая, если я пытаюсь решить моя проблема совершенно неверна, я хотел бы дать некоторый контекст, где мне нужно такое поведение. У меня есть приложение, которое использует TKinter для создания пользовательского интерфейса. Приложение должно поддерживать загрузку пользовательской конфигурации объекта, которая представляет собой скрипт python. Для сценария необходимо, чтобы была определена одна или несколько функций, которые предоставляют python объект с заданной c структурой (которая определяет пользовательский объект). Я загружаю указанный пользователем конфигурационный файл как модуль python и собираю все функции с указанными именами c в качестве пользовательских провайдеров сущностей. Затем я вызываю каждого провайдера конфигурации, преобразовываю результат в конфигурацию моей внутренней сущности и хочу добавить во время выполнения пункт меню, который должен создать экземпляр из соответствующей конфигурации сущности (создание экземпляра включает в себя несколько вызовов random, так что две сущности, которые создаются из один и тот же конфиг всегда разные). Итак, у меня есть dict (имя для внутренней конфигурации) с правильными значениями, но когда я добавляю пункт меню TKinter в foreach l oop поверх dict, все команды указывают на последнее значение итераторов:

custom_entities_menu = tk.Menu(entities_menu)
# ...
for name, cfg in custom_configs.items():
    custom_entities_menu.add_command(label = name, command = lambda: self.__create_new_entity(name, cfg))

1 Ответ

1 голос
/ 15 марта 2020

Functools или обязательные аргументы могут работать. Насколько я понимаю, functools более pythoni c:

import functools

cmds = list()

def main():
    values = {'label1':'value1', 'label2':'value2'}

    for l,v in values.items():
        add_command(command = functools.partial(apply_new_item, l,v))

def apply_new_item(label, value):
    print('{}: {}'.format(label, value))


def add_command(command):
    cmds.append(command)


def call_actions():
    for command in cmds:
        command()


# Create callbacks
main()
# Check callbacks
call_actions()

Он также будет работать с аргументами привязки в лямбда-выражении:

cmds = list()

def main():
    values = {'label1':'value1', 'label2':'value2'}

    for l,v in values.items():
        add_command(command = lambda l=l, v=v: apply_new_item(l,v))

def apply_new_item(label, value):
    print('{}: {}'.format(label, value))


def add_command(command):
    cmds.append(command)


def call_actions():
    for command in cmds:
        command()


# Create callbacks
main()
# Check callbacks
call_actions()

Редактировать: добавлены аргументы привязки в лямбда-выражения
Редактировать: функция functools перемещена частично вверх, потому что она больше pythoni c

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...