La réponse courte qui fonctionnera automatiquement.
J'ai créé une bibliothèque python pour résoudre ce problème spécifique. Elle est publiée sous licence libre, vous pouvez donc l'utiliser comme bon vous semble. Vous pouvez l'installer avec pip install seapie
ou consultez la page d'accueil ici https://github.com/hirsimaki-markus/SEAPIE
user@pc:home$ pip install seapie
from seapie import Seapie as seapie
def A():
b = 1
def B():
seapie(1, "b=2")
print(b)
B()
A()
sorties
2
les arguments ont la signification suivante :
- Le premier argument est la portée de l'exécution. 0 signifie local
B()
, 1 signifie parent A()
et 2 signifierait grand-parent <module>
aka global
- Le second argument est une chaîne de caractères ou un objet de code que vous voulez exécuter dans la portée donnée.
- Vous pouvez également l'appeler sans arguments pour un shell interactif dans votre programme
La réponse longue
C'est plus compliqué. Seapie fonctionne en éditant les cadres dans la pile d'appels en utilisant l'api CPython. CPython est le standard de facto, donc la plupart des gens n'ont pas à s'en soucier.
Les mots magiques qui vous intéressent probablement le plus si vous lisez ceci sont les suivants :
frame = sys._getframe(1) # 1 stands for previous frame
parent_locals = frame.f_locals # true dictionary of parent locals
parent_globals = frame.f_globals # true dictionary of parent globals
exec(codeblock, parent_globals, parent_locals)
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),ctypes.c_int(1))
# the magic value 1 stands for ability to introduce new variables. 0 for update-only
Cette dernière forcera les mises à jour à passer dans la portée locale. Les portées locales sont cependant optimisées différemment de la portée globale, de sorte que l'introduction de nouveaux objets a quelques problèmes lorsque vous essayez de les appeler directement s'ils ne sont pas initialisés de quelque manière que ce soit. Je vais copier quelques façons de contourner ces problèmes à partir de la page github.
- Assortir, importer et définir vos objets au préalable
- Attribution préalable d'un placeholder à vos objets
- Réassignez l'objet à lui-même dans le programme principal pour mettre à jour la table des symboles : x = locals()["x"]
- Utilisez exec() dans le programme principal au lieu de l'appeler directement pour éviter l'optimisation. Au lieu d'appeler x, faites : exec("x")
Si vous pensez qu'utiliser exec()
n'est pas quelque chose que vous voulez faire, vous pouvez émuler le comportement en mettant à jour le vrai dictionnaire local (pas celui retourné par locals()). Je vais copier un exemple de https://faster-cpython.readthedocs.io/mutable.html
import sys
import ctypes
def hack():
# Get the frame object of the caller
frame = sys._getframe(1)
frame.f_locals['x'] = "hack!"
# Force an update of locals array from locals dict
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),
ctypes.c_int(0))
def func():
x = 1
hack()
print(x)
func()
Sortie :
hack!