Utiliser exec() pour déclarer une variable globale

Le
Fabrice Delente
Bonjour.

J'ai défini une fonction qui ressemble à ceci :

def Valeur(nom,v):
exec("global "+nom)
exec(nom+"="+str(v))

mais quand j'exécute ce code :

Valeur("A",3)
Valeur("B",5)
Valeur("C",(A+B)/2)

les deux premières lignes passent correctement mais la troisième me donne

Valeur("C",(A+B)/2)
NameError: name 'A' is not defined

Est-il possible de régler ce problème ? Merci !

À bientôt.

--
Fabrice DELENTE

SVP, ne m'envoyez pas de pièces jointes aux formats Word, PowerPoint, RTF
(formats propriétaires). Utilisez des formats libres comme txt, html, ou
OpenOffice.Org, ou un format ouvert comme PDF. Merci. Voir
http://www.gnu.org/philosophy/no-word-attachments.fr.html
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Bruno Desthuilliers
Le #18628891
Fabrice Delente a écrit :
Bonjour.

J'ai défini une fonction qui ressemble à ceci :

def Valeur(nom,v):
exec("global "+nom)
exec(nom+"="+str(v))



Argl ! Quelle horreur !

mais quand j'exécute ce code :

Valeur("A",3)
Valeur("B",5)
Valeur("C",(A+B)/2)

les deux premières lignes passent correctement mais la troisième me donne

Valeur("C",(A+B)/2)
NameError: name 'A' is not defined

Est-il possible de régler ce problème ?



Oui:

A = 3
B = 5
C = (A + B) / 2

> Merci !

De rien !-)

Plus sérieusement: quel est ton cas d'utilisation ? Pourquoi cherches-tu
a faire d'une façon incompréhensible et compliquée quelque chose de (a
priori) fondamentalement simple ? Je me doute bien que tu a tes raisons,
mais il y a de fortes chances que tu sois parti sur une mauvaise
solution à un vrai problème. Les cas d'utilisation "valides" de exec ou
eval sont en pratique extrêmement rares - il y a le plus souvent (+ de
90% des cas que j'ai pu rencontré en quelques 8 années) une meilleure
façon de faire.
Fabrice Delente
Le #18632611
Bruno Desthuilliers
De rien !-)



Vraiment de rien alors :^)

Plus sérieusement: quel est ton cas d'utilisation ? Pourquoi cherches-tu
a faire d'une façon incompréhensible et compliquée quelque chose de (a
priori) fondamentalement simple ? Je me doute bien que tu a tes raisons,
mais il y a de fortes chances que tu sois parti sur une mauvaise
solution à un vrai problème. Les cas d'utilisation "valides" de exec ou
eval sont en pratique extrêmement rares - il y a le plus souvent (+ de
90% des cas que j'ai pu rencontré en quelques 8 années) une meilleure
façon de faire.



En fait c'est pour m'éviter d'écrire un parser...

Je voudrais utiliser un logiciel de géométrie dans l'espace (geospace); les
figures sont sauvegardées dans un fichier texte, par exemple

A point de coordonnées (0,0,0) dans le repère Rxyz
B point de coordonnées (0,8,0) dans le repère Rxyz

J'aimerais pouvoir scripter des calculs en python, puis écrire le résultat
de mes calculs dans un fichier que geospace pourra lire.

Par exemple, mon script python serait (en ayant définie la classe Point
« comme il faut »)

A=Point(0,0,0)
B=Point(0,8,0)
C=(A+B)/2

et python m'écrirait dans un fichier de sortie

A point de coordonnées (0,0,0) dans le repère Rxyz
B point de coordonnées (0,8,0) dans le repère Rxyz
C point de coordonnées (0,4,0) dans le repère Rxyz

Ma solution intermédiaire était de définir une fonction

def Point(nom,x,y,z):
OUTFILE.write("%s point de coordonnées (%g,%g,%g) dans le repère Rxyzn" %
(nom,x,y,z) )
exec("global "+nom)
exec(nom+"=Point("+str(x)+","+str(y)+","+str(z)+")")

de manière à avoir ma déclaration dans le fichier de sortie OUTFILE, et en
m^eme temps d'enregistrer la variable « nom » pour pouvoir la réutiliser
ensuite (par ex. C=(A+B)/2)

Voilà, j'espère avoir été clair... y a-t-il une solution à part écrire un
parser ?

À bientôt.

--
Fabrice DELENTE

SVP, ne m'envoyez pas de pièces jointes aux formats Word, PowerPoint, RTF
(formats propriétaires). Utilisez des formats libres comme txt, html, ou
OpenOffice.Org, ou un format ouvert comme PDF. Merci. Voir
http://www.gnu.org/philosophy/no-word-attachments.fr.html
Bruno Desthuilliers
Le #18633921
Fabrice Delente a écrit :
Bruno Desthuilliers
De rien !-)



Vraiment de rien alors :^)



Ah bin je fais pas semblant, hein !-)

Plus sérieusement: quel est ton cas d'utilisation ? Pourquoi cherches-tu
a faire d'une façon incompréhensible et compliquée quelque chose de (a
priori) fondamentalement simple ? Je me doute bien que tu a tes raisons,
mais il y a de fortes chances que tu sois parti sur une mauvaise
solution à un vrai problème. Les cas d'utilisation "valides" de exec ou
eval sont en pratique extrêmement rares - il y a le plus souvent (+ de
90% des cas que j'ai pu rencontré en quelques 8 années) une meilleure
façon de faire.



En fait c'est pour m'éviter d'écrire un parser...

Je voudrais utiliser un logiciel de géométrie dans l'espace (geospace); les
figures sont sauvegardées dans un fichier texte, par exemple

A point de coordonnées (0,0,0) dans le repère Rxyz
B point de coordonnées (0,8,0) dans le repère Rxyz

J'aimerais pouvoir scripter des calculs en python, puis écrire le résultat
de mes calculs dans un fichier que geospace pourra lire.

Par exemple, mon script python serait (en ayant définie la classe Point
« comme il faut »)

A=Point(0,0,0)
B=Point(0,8,0)
C=(A+B)/2

et python m'écrirait dans un fichier de sortie

A point de coordonnées (0,0,0) dans le repère Rxyz
B point de coordonnées (0,8,0) dans le repère Rxyz
C point de coordonnées (0,4,0) dans le repère Rxyz

Ma solution intermédiaire était de définir une fonction

def Point(nom,x,y,z):
OUTFILE.write("%s point de coordonnées (%g,%g,%g) dans le repère Rxyzn" %
(nom,x,y,z) )
exec("global "+nom)
exec(nom+"=Point("+str(x)+","+str(y)+","+str(z)+")")



Attention, en Python, les fonctions sont des objets, les classes aussi,
et il n'y a pas de namespace séparés selon le type d'objet. Donc, tu ne
peux pas avoir dans la même portée une fonction *et* une classe homonymes.

Accessoirement aussi: le formatage de chain, c'est pas mal. Au lieu de
"toto=" + str(a) + "-" + str(b)

tu peux faire:
"toto=%s-%s" % (a, b)


de manière à avoir ma déclaration dans le fichier de sortie OUTFILE, et en
m^eme temps d'enregistrer la variable « nom » pour pouvoir la réutiliser
ensuite (par ex. C=(A+B)/2)



Ok. C'est effectivement un cas assez particulier. Mais - même s'il est
possible d'ecrire ta fonction setPoint(nom, x, y, z) de manière à ce
qu'elle modifie l'espace de nommage global, ça ne servira à rien dans le
cas d'expressions comme C=(A+B)/2.

Bref, la solution simple (AMHA) est de:

1/ definir une *classe* point qui va bien
2/ créer les instances normalement, ie:

A = Point(1, 2, 2)
B = Point(2, 0, 0)

3/ définir les opérateurs qui vont bien (__add__, __mul__ etc) pour
supporter les expressions de type "C=(A+B)/2"

4/ écrire une fonction qui collecte les points dans un espace de nommage
donné pour les écrire dans un fichier:

def sauver_points(ns, outfile):
tpl = "point %s de coordonnees (%s, %s, %s)"
for k, v in ns.iteritems():
if isinstance(v, Point):
outfile.write(tpl % (k, v.x, v.y, v.z)


Si tu préfère, tu peux aussi la définir plutôt en tant que classmethod
de Point:

class Point(object):
# code ici

@classmethod
def write(cls, ns, outfile):
tpl = "point %s de coordonnees (%s, %s, %s)"
for k, v in ns.iteritems():
if isinstance(v, cls):
outfile.write(tpl % (k, v.x, v.y, v.z)


A = Point(1, 2, 2)
B = Point(2, 0, 0)
C = (A+B)/2

with open('path/to/file', 'w') as outfile:
Point.write(globals(), outfile)

Ok, ça t'oblige à passer explicitement l'espace de nommage et le fichier
de sortie. Le problème, c'est que la notion de "portée globale" en
Python est en réalité restreinte au module. Donc, quand tu appelles
globals() dans une fonction définie dans un module, c'est à l'espace de
nommage *de ce module* que tu accèdes.

D'un autre côté, passer explicitement le namespace permet d'utiliser ces
fonctionnalités dans un autre contexte, ce qui peut (ou non) être un plus...

Voili voila... Mes deux centimes.

NB : si l'ordre de création des points est important, ça peut se
solutionner aussi.
Bruno Desthuilliers
Le #18634101
Bruno Desthuilliers a écrit :
(snip)

Désolé, pb d'indentation. Il fallait bien sûr lire:

class Point(object):
# code ici

@classmethod
def write(cls, ns, outfile):
tpl = "point %s de coordonnees (%s, %s, %s)"
for k, v in ns.iteritems():
if isinstance(v, cls):


outfile.write(tpl % (k, v.x, v.y, v.z)
Fabrice Delente
Le #18634971
Bruno Desthuilliers
def sauver_points(ns, outfile):
tpl = "point %s de coordonnees (%s, %s, %s)"
for k, v in ns.iteritems():
if isinstance(v, Point):
outfile.write(tpl % (k, v.x, v.y, v.z)




Ok, ça t'oblige à passer explicitement l'espace de nommage et le fichier
de sortie. Le problème, c'est que la notion de "portée globale" en
Python est en réalité restreinte au module. Donc, quand tu appelles
globals() dans une fonction définie dans un module, c'est à l'espace de
nommage *de ce module* que tu accèdes.



Ok, comme j'avais défini ma fonction Point dans un module que j'importais,
ça peut peut-^etre expliquer...

D'un autre côté, passer explicitement le namespace permet d'utiliser ces
fonctionnalités dans un autre contexte, ce qui peut (ou non) être un plus...



Oui, mais comment déterminer le namespace que je dois passer à
sauver_points() ?

A=Point(0,0,0)
B=Point(0,8,0)
C=(A+B)/2
sauver_point(???,outfile)

Comment savoir ce que je dois mettre à la place de ??? ?

À bientôt.

--
Fabrice DELENTE

SVP, ne m'envoyez pas de pièces jointes aux formats Word, PowerPoint, RTF
(formats propriétaires). Utilisez des formats libres comme txt, html, ou
OpenOffice.Org, ou un format ouvert comme PDF. Merci. Voir
http://www.gnu.org/philosophy/no-word-attachments.fr.html
Bruno Desthuilliers
Le #18635901
Fabrice Delente a écrit :
Bruno Desthuilliers
def sauver_points(ns, outfile):
tpl = "point %s de coordonnees (%s, %s, %s)"
for k, v in ns.iteritems():
if isinstance(v, Point):
outfile.write(tpl % (k, v.x, v.y, v.z)




Ok, ça t'oblige à passer explicitement l'espace de nommage et le fichier
de sortie. Le problème, c'est que la notion de "portée globale" en
Python est en réalité restreinte au module. Donc, quand tu appelles
globals() dans une fonction définie dans un module, c'est à l'espace de
nommage *de ce module* que tu accèdes.



Ok, comme j'avais défini ma fonction Point dans un module que j'importais,
ça peut peut-^etre expliquer...

D'un autre côté, passer explicitement le namespace permet d'utiliser ces
fonctionnalités dans un autre contexte, ce qui peut (ou non) être un plus...



Oui, mais comment déterminer le namespace que je dois passer à
sauver_points() ?

A=Point(0,0,0)
B=Point(0,8,0)
C=(A+B)/2
sauver_point(???,outfile)

Comment savoir ce que je dois mettre à la place de ??? ?



Si tu es dans une fonction, et que tu ne veux que les points définis
dans la fonction, utilise locals().

Si tu es dans une fonction et que tu veux les points définis au
top-level du module (ou script, pareil) *dans lequel la fonction est
définie*, utilise globals().

Si tu es au top-level (du script, du module, de l'interpreteur...),
locals() et globals() retournent la même chose, donc c'est indifférent.
Fabrice Delente
Le #18636111
Bruno Desthuilliers
Si tu es dans une fonction, et que tu ne veux que les points définis
dans la fonction, utilise locals().

Si tu es dans une fonction et que tu veux les points définis au
top-level du module (ou script, pareil) *dans lequel la fonction est
définie*, utilise globals().

Si tu es au top-level (du script, du module, de l'interpreteur...),
locals() et globals() retournent la même chose, donc c'est indifférent.



Ok, merci pour toutes ces précisions, je vais pouvoir m'y remettre :^)

À bientôt.

--
Fabrice DELENTE

SVP, ne m'envoyez pas de pièces jointes aux formats Word, PowerPoint, RTF
(formats propriétaires). Utilisez des formats libres comme txt, html, ou
OpenOffice.Org, ou un format ouvert comme PDF. Merci. Voir
http://www.gnu.org/philosophy/no-word-attachments.fr.html
Publicité
Poster une réponse
Anonyme