«NameError: глобальное имя не определено» в pdb для существующего словаря

Я столкнулся с проблемой, связанной с областями в функции lambda. Я могу успешно вывести foo на стандартный вывод, но получаю сообщение об ошибке при использовании max(), включая lambda - см. упрощенный код ниже...

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

(Pdb) foo = self.some_method()    # some_method() returns a dict, printed in the next step

(Pdb) pp foo

{'1': {'count': 1,
       'extra_data': {'activity-count': 1,
                             'budget': 0,
                             [...MORE KEY-VALUE PAIRS HERE...]
                             'version': 1},
       [...LOTS MORE KEY-VALUE PAIRS HERE...]
       'elements_total': defaultdict(<type 'int'>, {'result': 1, 'another_key': 2}),
       'extra_year_data': defaultdict(<function <lambda> at 0x10e05bd70>, {})}, 

 '2': {'count': 1,
       'extra_data': {'activity-count': 1,
                             'budget': 3,
                             [...MORE KEY-VALUE PAIRS HERE...]
                             'version': 1},
       [...LOTS MORE KEY-VALUE PAIRS HERE...]
       'elements_total': defaultdict(<type 'int'>, {'result': 1, 'another_key': 2}),
       'extra_year_data': defaultdict(<function <lambda> at 0x10e05bd70>, {})}}

(Pdb) max(foo, key=lambda x: foo[x]['extra_data']['budget'])
*** NameError: global name 'foo' is not defined

В общем, я пытаюсь использовать max(foo, key=lambda x: foo[x]['extra_data']['budget']), чтобы найти наибольшее значение для вложенного ключа budget в пределах неизвестного числа ключей первого порядка.

Ожидаемый результат в этом случае может быть 2 как значение для foo['2']['extra_data']['budget'] = 3 против foo['1']['extra_data']['budget'] = 0.

Может ли ошибка быть связана с тем, что некоторые из (несвязанных) ключей содержат defaultdict внутри них?


person user2761030    schedule 02.02.2016    source источник
comment
Возможно, я неправильно понял, что вы имели в виду под средой, но это было запущено с pdb в терминале на python 2.7.6 на Mac 10.10.1 Yosemite.   -  person user2761030    schedule 02.02.2016
comment
Нет, разобрался. Все, что мне было нужно, это еще немного кофеина.   -  person Martijn Pieters    schedule 02.02.2016
comment
Почему не max(foo.values(), key=lambda x: x['extra_data']['budget'])?   -  person Burhan Khalid    schedule 02.02.2016


Ответы (3)


Вы задали новый local с помощью pdb, но он не виден для выражений, использующих вложенные области в этом сеансе отладчика. Любое выражение во вложенной области видимости, такое как lambda, используемое для аргумента key, использующее имя, локальное для текущего фрейма, должно быть замыканием и будет иметь эту проблему.

Это ограничение работы отладчика и компиляции Python; замыкания могут быть созданы только в том случае, если функция, которая должна их создавать, была скомпилирована в том же сеансе. Поскольку функция, которую вы отлаживаете, была скомпилирована без замыкания foo, она не может использоваться выражением lambda как таковым.

Вы можете привязать локальный объект к лямбде (сделав его локальным, а не замыкающим):

max(foo, key=lambda x, foo=foo: foo[x]['extra_data']['budget'])

См. Что именно содержится в объекте obj.__closure__? для подробности о том, как компилятор Python создает замыкания.

person Martijn Pieters    schedule 02.02.2016
comment
Спасибо, кажется, теперь я это понимаю. Означает ли это, что когда я добавляю это в свой скрипт, мне не нужно привязывать локалку к лямбда-т.е. я могу просто использовать max(foo, key=lambda x: foo[x]['extra_data']['budget']) ? - person user2761030; 02.02.2016
comment
@ user2761030: извините, попробую еще раз. Да, вы можете добавить это в свой сценарий, и поскольку компилятор Python затем увидит имя foo в используемом lambda, он создаст замыкание в родительской области. Тогда это сработает. Вы просто не можете сделать это в pdb. - person Martijn Pieters; 02.02.2016

Существует отчет об ошибке для Python 3 (однако эта проблема затрагивает Python 2.7 так же, как вы узнали), который предлагает обходной путь в качестве альтернативы решению Мартейна: interact в ответ на приглашение pdb переводит вас в интерактивный сеанс который заполнен globals() и locals(), и ваш lambda должен работать как положено.

person kynan    schedule 20.10.2016

Это испортит вашу глобальную область, но это быстрый (грязный) обходной путь, который я использую в этом случае при использовании python 2.7:

globals().update(locals())
person olivecoder    schedule 04.03.2020
comment
У меня тоже работало в python 3.6 - person S. Kaczor; 21.05.2020