2010-06-23 4 views
1

(simplifié d'une question trop bavard je posté plus tôt!)générer dynamiquement une fonction de générateur d'un blob de texte

d'une chaîne Python contenant du code Python valide qui contient une déclaration « rendement », comment puis-je construire un générateur que exec est cette chaîne?

Par exemple, compte tenu de la chaîne:

code_string = """for x in range(0, 10): 
    yield x 
""" 

Je veux construire un générateur f qui exécute code_string tel que (dans cet exemple particulier):

assert(list(f()) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 

Notez que code_string est arbitraire, donc cette assertion est uniquement valable pour l'exemple ci-dessus. code_string peut contenir n'importe quel code python valide contenant une instruction yield.

Merci!

Edit:

La première solution que je pensais était juste munge "def f():" dans la chaîne et en retrait chaque ligne par programmation. Cependant, cela échoue si code_string utilise une indentation différente. J'espérais qu'il y avait quelques functools kung-fu peu connus qui peuvent construire une fonction à partir d'une goutte de texte.

Edit2:

J'ai essayé aussi un exec dans une fonction comme ceci:

code = "for x in range(0, 10): yield x" 
def f(): 
    exec code in globals(), locals() 

Cela se traduit par "SyntaxError: 'rendement' en dehors de la fonction"

Résolu: Problème Je suis corrigé, l'indentation est relative, de sorte que cela fonctionne:

code_string = """for x in range(0, 10): 
    yield x 
""" 

exec "def f():\n" + [(" " + line) for line in code_string.split('\n')]) + "\n" 
assert list(f()) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
+2

Croyez-moi, ce n'est pas la partie du problème que vous voulez résoudre. –

+0

:-) J'apprécie le conseil, mais j'aime résoudre les problèmes. –

+0

J'ai supprimé ma réponse, car je ne sais pas comment la faire fonctionner (je pense que le exec() n'était appelé que lorsque la fonction produite par la fermeture était appelée, au lieu de l'appel de la fonction de production). Je suggérerais de trouver un autre moyen de résoudre ce problème, comme l'a suggéré Ignacio. – Wilduck

Répondre

2

Qu'est-ce que ça peut vous faire ce que l'indentation est utilisé dans la fonction? L'indentation est relative et n'a pas besoin d'être cohérente. Tack "def f(): \ n" sur le devant, ajoutez un espace à chaque ligne de votre fonction et exécutez-le, et vous avez terminé. Cela fonctionne aussi bien que votre réponse.

exec "def f():\n " + " \n".join(code_string.splitlines()) + "\n" 

(Désolé d'entendre votre solution pyparsing était trop compliquée On dirait que vous essayez de recréer une grammaire complète Python juste pour ajouter une fonctionnalité ou deux à la langue. Dans ce cas, au lieu d'analyser le tout à l'aide de parseString, vous pouvez simplement ajouter une syntaxe spécialisée, définir une expression pypars pour juste cette syntaxe, écrire une action d'analyse qui crée du code Python, et utiliser transformString pour rechercher et remplacer la syntaxe spéciale avec le comportement Python correspondant Ensuite, vous pouvez exec ou compiler tout le module transformé, avec juste un peu de pyparsing.)

+0

Alors c'est! J'étais sous l'impression (erronée) que l'indentation devait être cohérente tout au long d'un module de compilation. Merci. –

1

Essayez:

exec("""def f(): 
     for x in range(0, 10): 
       yield x 
""") 


for x in f(): 
    print x 
+0

Merci, cela fonctionne bien si le code transmis utilise des retraits à 4 espaces. Cependant, Python valide peut également utiliser des indentations à deux espaces. Je suis à la recherche d'une solution qui fonctionne avec des valeurs arbitraires pour code_string. –

+0

Je viens de penser à cela ... il est assez facile de détecter l'indentation avec une expression régulière. max (min ([indentations]), 4) donnera une valeur correcte. –

Questions connexes