Exécuter du code Python contenu dans une chaîne
Question
J'écris un moteur de jeu en utilisant pygame et box2d et, dans l'éditeur de personnages, je veux pouvoir écrire le code qui sera exécuté lors d'événements de raccourci.
Mon plan était d'avoir un éditeur de texte dans le générateur de caractères vous permettant d'écrire du code similaire à:
if key == K_a:
## Move left
pass
elif key == K_d:
## Move right
pass
Je vais récupérer le contenu de l'éditeur de texte sous forme de chaîne et je veux que le code soit exécuté dans une méthode de cette méthode de Caractère:
def keydown(self, key):
## Run code from text editor
Quelle est la meilleure façon de faire cela?
La solution
Vous pouvez utiliser le eval (chaîne)
méthode pour le faire.
Définition
eval (code, globals = None, locals = None)
Le code est simplement du code Python standard - cela signifie qu'il doit toujours être correctement mis en retrait.
Les globales peuvent avoir un __ intégré __
personnalisé, ce qui pourrait être utile à des fins de sécurité.
Exemple
eval("print('Hello')")
Affiche hello
sur la console. Vous pouvez également spécifier des variables locales et globales pour le code à utiliser:
eval("print('Hello, %s'%name)", {}, {'name':'person-b'})
Problèmes de sécurité
Soyez prudent, cependant. Toute entrée de l'utilisateur sera exécutée. Considérez:
eval("import os;os.system('sudo rm -rf /')")
Il y a plusieurs façons de contourner ce problème. Le plus simple est de faire quelque chose comme:
eval("import os;...", {'os':None})
Ce qui lancera une exception, plutôt que d’effacer votre disque dur. Bien que votre programme soit un ordinateur de bureau, cela pourrait poser un problème si les gens redistribuaient les scripts, ce que j’imagine bien vouloir.
Exemple étrange
Voici un exemple d'utilisation de eval
plutôt étrange:
def hello() : print('Hello')
def world() : print('world')
CURRENT_MOOD = 'happy'
eval(get_code(), {'contrivedExample':__main__}, {'hi':hello}.update(locals()))
Ce que cela fait sur la ligne d'évaluation est:
- Donne un autre nom au module actuel (il devient
contrivedExample
dans le script). Le consommateur peut appelercontrivedExample.hello ()
maintenant.) - Cela définit
hi
comme pointant surhello
- Il a combiné ce dictionnaire avec la liste des globales actuelles du module en cours d'exécution.
ECHEC
Il s'avère (merci aux commentateurs!) que vous devez réellement utiliser l'instruction exec
. Big oops. Les exemples révisés sont les suivants:
exec
Définition
(Cela semble familier!)
Exec est une déclaration:
exec " code " [dans le champ d'application]
Où scope est un dictionnaire de variables locales et globales. S'il n'est pas spécifié, il s'exécute dans la portée actuelle.
Le code est simplement du code Python standard. Cela signifie qu'il doit encore être mis en retrait correctement.
exec
Exemple
exec "print('hello')"
Affiche hello
sur la console. Vous pouvez également spécifier des variables locales et globales pour le code à utiliser:
eval "print('hello, '+name)" in {'name':'person-b'}
exec
Problèmes de sécurité
Soyez prudent, cependant. Toute entrée de l'utilisateur sera exécutée. Considérez:
exec "import os;os.system('sudo rm -rf /')"
Imprimer la déclaration
Comme l'ont également noté les commentateurs, print
est une instruction dans toutes les versions de Python antérieures à la version 3.0. En 2.6, le comportement peut être modifié en tapant à partir de __future__ import print_statement
. Sinon, utilisez:
print "hello"
Au lieu de:
print("hello")
Autres conseils
Comme d'autres l'ont déjà fait remarquer, vous pouvez charger le texte dans une chaîne et utiliser exec "codestring"
. S'il est déjà contenu dans un fichier, utilisez execfile pour éviter de le charger.
Une note de performance: évitez d'exécuter le code plusieurs fois, car l'analyse et la compilation de la source python sont des processus lents. c'est à dire. ne pas avoir:
def keydown(self, key):
exec user_code
Vous pouvez améliorer un peu cela en compilant la source dans un objet code (avec compile ()
) et l'exécuter, ou mieux, en construisant une fonction que vous conservez, et ne le construisez qu'une seule fois. Soit obliger l’utilisateur à écrire "def my_handler (args ...)" ou à l’ajouter soi-même et à faire quelque chose comme:
user_source = "def user_func(args):\n" + '\n'.join(" "+line for line in user_source.splitlines())
d={}
exec user_source in d
user_func = d['user_func']
Puis plus tard:
if key == K_a:
user_func(args)
Vous pouvez utiliser eval ()
eval ou exec. Vous devez absolument lire les références de la bibliothèque Python avant de programmer.