2009-05-16 5 views
5

Je veux exécuter du code Python, tapé à l'exécution, donc j'obtenir la chaîne et appelezUtiliser exec() avec des fonctions récursives

exec(pp, globals(), locals())

pp est la chaîne. Cela fonctionne bien, sauf pour les appels récursifs, e. . G, par exemple, ce code est OK:

def horse(): 
    robot.step() 
    robot.step() 
    robot.turn(-1) 
    robot.step() 

while True: 
    horse() 

Mais celui-ci n'est pas:

def horse(): 
    robot.step() 
    robot.step() 
    robot.turn(-1) 
    robot.step() 
    horse() 

horse() 

NameError: global name 'horse' is not defined

Est-il possible d'exécuter du code récursif ainsi?

MISE À JOUR

a = """\ 
def rec(n): 
    if n > 10: 
     return 
    print n 
    return rec(n+1) 

rec(5)""" 

exec(a) 

Works si elles sont mises sur le haut niveau. Mais si elle est déplacée dans une fonction:

def fn1(): 
    a = """\ 
def rec(n): 
    if n > 10: 
     return 
    print n 
    return rec(n+1) 

rec(5)""" 

    exec(a) 

fn1() 

la même erreur se produit: NameError: nom global 'rec' est pas défini

+0

S'il vous plaît dites-moi cette chaîne exec ne provient pas d'utilisateur contribution. –

+0

@Nadia, pourquoi, oui, c'est _is_ :) – Headcrab

Répondre

4

Il fonctionne pour moi:

a = """\ 
def rec(n): 
    if n > 10: 
     return 
    print n 
    return rec(n+1) 

rec(5)""" 

exec(a) 
5 
6 
7 
8 
9 
10 

Tout ce que je peux dire est qu'il y a probablement un bug dans votre code.

Modifier

Ici, vous allez

def fn1(): 
    glob = {} 
    a = """\ 
def rec(n): 
    if n > 10: 
     return 
    print n 
    return rec(n+1) 

rec(5)""" 
    exec(a, glob) 

fn1() 
+0

Eh bien, pas exactement - voir la mise à jour ci-dessus ... – Headcrab

+0

Pourquoi ça marche? – Headcrab

+0

@Headcrab, car il vous donne un dictionnaire global et local éditable. locals() vous donne un dictionnaire qui ne peut pas changer les "vraies" variables locales. – Unknown

0

"NameError: nom global 'rec' est pas défini" signifie qu'il cherche rec dans la portée globale, pas la portée locale. On dirait que c'est la définition de rec dans la portée locale, mais ensuite tenter d'exécuter dans le global. Essayez d'imprimer les locals() et globals() dans la chaîne que vous êtes en train d'exécuter.

More info.

3

Cela fonctionne pour moi (ajouté global rec). rec(5) appelle le rec local, mais rec(n+1) appelle un rec global (qui n'existe pas) sans elle.

def fn1(): 
    a = """global rec 
def rec(n): 
    if n > 10: 
     return 
    print n 
    return rec(n+1) 

rec(5)""" 

    exec(a) 
4

Cela m'a surpris aussi d'abord, et semble être un cas d'angle bizarre où exec agit ni tout à fait comme une définition de haut niveau, ou une définition au sein d'une fonction englobante. Il semble que ce qui se passe est que la définition de la fonction est en train d'être exécutée dans les locals() dict que vous passez. Cependant, la fonction définie n'a pas réellement accès à cette fonction locale.

Normalement, si vous définissez une fonction au premier niveau, les locals et les globals sont les mêmes, donc les fonctions sont visibles à l'intérieur car elles peuvent voir la fonction dans les globals. Lorsqu'une fonction est définie dans la portée d'une autre fonction, python remarquera qu'elle est accessible dans la fonction et créera une fermeture de sorte que "horse" soit mappé à la liaison dans la portée externe.

Ici, c'est un étrange cas à mi-chemin.exec agit comme si les définitions étaient au niveau le plus élevé, donc aucune fermeture n'est créée. Cependant, puisque les locals ne sont pas les mêmes que les globals, la définition ne va pas là où la fonction peut y accéder - elle n'est définie que dans les dictons locaux inaccessibles.

Il y a quelques choses que vous pourriez faire:

  1. Utilisez le même dictionnaire pour les habitants et globals. c'est-à-dire "exec s in locals(),locals()" (ou mieux, utilisez simplement votre propre dict). Fournir seulement un dicton globals() a le même effet - par exemple "exec s in mydict" #
  2. Mettez le func dans sa propre fonction, de sorte qu'une fermeture soit créée. par exemple

    s=""" 
    def go(): 
        def factorial(x): 
         if x==0: return 1 
         return x*factorial(x-1) 
        print factorial(10) 
    go()""" 
    
  3. Force de la fonction d'entrer dans globals() plutôt que la population locale en mettant une directive « FUNCNAME globale », comme l'a suggéré stephan's answer

Questions connexes