Question

Veuillez excuser le titre vague. Si quelqu'un a une suggestion, s'il vous plaît faites le moi savoir! Aussi, s'il vous plaît retag avec des balises plus appropriées!

Le problème

Je souhaite qu'une instance d'une classe importée puisse afficher des éléments du domaine (globaux, locaux) de l'importateur. Puisque je ne suis pas sûr du mécanisme exact à l'œuvre ici, je peux le décrire beaucoup mieux avec des extraits que des mots.

## File 1
def f1():  print "go f1!"

class C1(object):
    def do_eval(self,x):  # maybe this should be do_evil, given what happens
        print "evaling"
        eval(x)
        eval(x,globals(),locals())

Puis lancez ce code depuis une session iteractive, il y aura beaucoup de NameErrors

## interactive
class C2(object):
    def do_eval(self,x):  # maybe this should be do_evil, given what happens
        print "evaling"
        eval(x)
        eval(x,globals(),locals())

def f2():
    print "go f2!"

from file1 import C1
import file1

C1().do_eval('file1.f1()')
C1().do_eval('f1()')
C1().do_eval('f2()')

file1.C1().do_eval('file1.f1()')
file1.C1().do_eval('f1()')
file1.C1().do_eval('f2()')

C2().do_eval('f2()')
C2().do_eval('file1.f1()')
C2().do_eval('f1()')

Existe-t-il un idiome / modèle commun pour ce type de tâche? Suis-je en train d'aboyer complètement le mauvais arbre?

Était-ce utile?

La solution

Dans cet exemple, vous pouvez simplement transférer des fonctions en tant qu'objets aux méthodes dans C1 :

>>> class C1(object):
>>>    def eval(self, x):
>>>        x()
>>>
>>> def f2(): print "go f2"
>>> c = C1()
>>> c.eval(f2)
go f2

En Python, vous pouvez transmettre des fonctions et des classes à d'autres méthodes et les appeler / les créer à cet endroit.

Si vous voulez réellement évaluer une chaîne de code, vous devez spécifier l'environnement, comme déjà mentionné par Thomas.

Votre module par le haut, légèrement modifié:

## File 1
def f1():  print "go f1!"

class C1(object):
    def do_eval(self, x, e_globals = globals(), e_locals = locals()):
        eval(x, e_globals, e_locals)

Maintenant, dans l'interprète interactif:

>>> def f2():
>>>    print "go f2!"
>>> from file1 import *    # 1
>>> C1().do_eval("f2()")   # 2
NameError: name 'f2' is not defined

>>> C1().do_eval("f2()", globals(), locals()) #3
go f2!
>>> C1().do_eval("f1()", globals(), locals()) #4
go f1!

Quelques annotations

  1. Ici, nous insérons tous les objets de fichier1 dans l'espace de noms de ce module
  2. f2 n'est pas dans l'espace de noms de fichier1 , nous obtenons donc un NameError
  3. Nous passons maintenant explicitement dans l'environnement et le code peut être évalué
  4. f1 est dans l'espace de noms de ce module, car nous l'avons importé

Modifier : exemple de code ajouté sur la manière de passer explicitement l'environnement pour eval .

Autres conseils

Les fonctions sont toujours exécutées dans l'étendue dans laquelle elles sont définies, de même que les méthodes et les corps de classe. Ils ne sont jamais exécutés dans une autre portée. Comme l'importation n'est qu'une autre instruction d'affectation et que tout ce qui se trouve dans Python est une référence, les fonctions, les classes et les modules ne savent même pas où ils sont importés.

Vous pouvez faire deux choses: transmettre explicitement "l'environnement" que vous souhaitez qu'ils utilisent ou utiliser le piratage de pile pour accéder à l'espace de noms de l'appelant. Le premier est largement préféré au second, car il n’est pas aussi dépendant de la mise en œuvre et fragile que le dernier.

Vous voudrez peut-être consulter la classe string.Template, qui tente de faire quelque chose de similaire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top