2016-02-02 1 views
2

J'ai rencontré un problème concernant les étendues dans une fonction lambda. Je peux foo avec succès à stdout sortie mais je reçois une erreur lors de l'utilisation max() y compris un lambda - voir code simplifié ci-dessous ...'NameError: nom global n'est pas défini' sous pdb, pour le dictionnaire existant

Dans l'ensemble, je suis en train trouver la plus grande valeur pour une budget clé imbriquée dans un nombre inconnu des clés de premier ordre.

(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 

Dans l'ensemble, je suis en train d'utiliser max(foo, key=lambda x: foo[x]['extra_data']['budget']) pour trouver la plus grande valeur pour une budget clé imbriquée dans un nombre inconnu de premières touches de commande.

Le résultat attendu dans ce cas pourrait être 2 comme valeur pour foo['2']['extra_data']['budget'] = 3 par rapport à foo['1']['extra_data']['budget'] = 0.

L'erreur peut-elle être liée au fait que certaines des clés (non liées) ont defaultdict s à l'intérieur de celles-ci?

+0

J'ai peut-être mal compris ce que vous vouliez dire par environnement, mais cela a été exécuté avec pdb dans terminal sur python 2.7.6 sur Mac 10.10.1 Yosemite. – user2761030

+0

Non, sussed it out. Tout ce dont j'avais besoin était un peu plus de caféine. –

+0

Pourquoi pas 'max (foo.values ​​(), clé = lambda x: x ['extra_data'] ['budget'])'? –

Répondre

7

Vous avez défini un nouveau local avec pdb, mais cela n'est pas visible pour les expressions utilisant des étendues imbriquées dans cette session de débogage. Toute expression dans une portée imbriquée telle que lambda utilisée pour l'argument key, en utilisant un nom qui est local à la trame en cours, devrait être une fermeture et aura ce problème.

C'est une limitation du fonctionnement du débogueur et de la compilation Python; les fermetures ne peuvent être créées que si la fonction qui a besoin de les produire a été compilée dans la même session. Puisque la fonction que vous déboguez a été compilée sans foo étant une fermeture, elle ne peut pas être utilisée par l'expression lambda en tant que telle.

Vous pouvez lier le local au lambda (ce qui en fait un local plutôt qu'une fermeture):

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

Voir What exactly is contained within a obj.__closure__? pour plus de détails sur la façon dont le compilateur Python crée des fermetures.

+0

Merci, je pense que je comprends maintenant. Cela signifie-t-il que lorsque j'ajoute ceci à mon script, je n'ai pas besoin de lier le local au lambda - ie je peux simplement utiliser 'max (foo, clé = lambda x: foo [x] ['extra_data'] [ 'budget']) '? – user2761030

+0

@ user2761030: désolé, j'essaie à nouveau. Oui, vous pouvez ajouter ceci à votre script, et parce que le compilateur Python voit alors le nom 'foo' dans le' lambda' utilisé, il créera la fermeture dans la portée parent. Ça va alors fonctionner. Vous ne pouvez pas le faire dans 'pdb'. –

1

Il y a un bug report for Python 3 (mais ce problème affecte Python 2.7 ainsi que vous avez découvert) ce qui suggère un workaround comme une alternative à la solution de Martijn: interact à l'invite pdb vous dépose dans une session interactive qui est peuplée avec globals()etlocals() et votre lambda devrait fonctionner comme prévu.