2009-10-07 8 views
0

Je veux réellement créer un nouveau local. Je sais que cela semble douteux, mais je pense que j'ai un bon cas d'utilisation pour cela. Essentiellement mon problème est que ce code jette « NameError: nom global « œufs » ne sont pas définis » lorsque je tente d'imprimer des œufs:Existe-t-il un moyen d'affecter les locaux au moment de l'exécution?

def f(): 
    import inspect 
    frame_who_called = inspect.stack()[1][0] 
    frame_who_called.f_locals['eggs'] = 123 

def g(): 
    f() 
    print(eggs) 

g() 

J'ai trouvé cette vieille chose: http://mail.python.org/pipermail/python-dev/2005-January/051018.html

Ce qui voudrait dire que je pourrait être en mesure de le faire en utilisant ctypes et en appelant une fonction secrète, mais ils ont seulement parlé de mettre à jour une valeur. Mais peut-être y a-t-il un moyen plus facile?

+0

Ce code fonctionne en python 2.5.4, donc il semble que py3k a changé quelque chose ici. – liori

+0

Je remets en question votre utilisation de "gentil" en référence au cas d'utilisation associé. Pourriez-vous élaborer? –

+0

Construire un DSL en utilisant une syntaxe de type python en python. C'est assez sympa pour moi. – liori

Répondre

2

Comme Greg Hewgill mentionné dans un commentaire sur la question, j'ai répondu another question sur la modification locals en Python 3, et je vais donner un peu d'un résumé ici.

Il existe un post on the Python 3 bug list sur ce problème - il est un peu mal documenté dans les manuels Python 3. Python 3 utilise un tableau pour les locaux au lieu d'un dictionnaire comme dans Python 2 - l'avantage est un temps de recherche plus rapide pour les variables locales (Lua fait cela aussi). Fondamentalement, le tableau est défini à "bytecode-compile-time" et ne peut pas être modifié à l'exécution.

Voir en particulier le dernier paragraphe Georg Brandl's post on the bug list pour plus de détails plus fins pourquoi cela ne peut pas (et ne sera probablement jamais) en Python 3.

3

Je suis très curieux quant à votre cas d'utilisation. Pourquoi diable essayez-vous piquez une nouvelle locale dans le cadre de l'appelant, plutôt que de simplement faire quelque chose comme ceci:

def f(): 
    return 123 

def g(): 
    eggs = f() 
    print(eggs) 

Après tout, vous pouvez retourner un tuple avec autant de valeurs que vous le souhaitez:

def f(): 
    return 123, 456, 789 

def g(): 
    eggs, ham, bacon = f() 
    print(eggs, ham, bacon) 
+0

Parce que vous pourriez vouloir piquer des centaines de variables de cette façon. Vous ne voulez pas le faire manuellement. – liori

+5

Pourquoi voudriez-vous piquer des centaines de variables? Renvoie une instance de classe avec des centaines de membres. Il est moins susceptible de vous rendre fou, et plus important encore, moins susceptible de rendre fou n'importe qui d'autre essayant de comprendre et de maintenir votre code à l'avenir. – steveha

+0

Lors de la création de lignes DSL en étendant Python. Dans ce cas, vous jetez déjà beaucoup d'hypothèses sur le code et placez un ensemble d'hypothèses différent. – liori

1

En Python 2. *, vous pouvez obtenir ce code pour en battant la normale optimisation des habitants:

>>> def g(): 
... exec 'pass' 
... f() 
... print(eggs) 

La présence d'une cause déclaration exec Python 2 pour compiler g de façon totalement non optimisé, donc les habitants sont dans un dict au lieu de dans un tableau comme ils le feraient normalement. (Le coup de performance peut être considérable).

Cette "désoptimisation" n'existe pas dans Python 3, où exec n'est plus une instruction (pas même un mot-clé, juste une fonction) - même mettre des parenthèses après ça n'aide pas ...:

>>> def x(): 
... exec('a=23') 
... print(a) 
... 
>>> x() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 3, in x 
NameError: global name 'a' is not defined 
>>> 

-à-dire, pas même exec peut maintenant « créer » les habitants qui ont été comprirent pas def -temps (lorsque le compilateur a fait son laissez-passer pour tourner le corps de la fonction en bytecode).

Votre meilleur pari serait d'abandonner. Le deuxième meilleur serait d'avoir votre fonction f injecter de nouveaux noms dans le globals de l'appelant - ces sont toujours un dict, après tout.