Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Utiliser exec() pour déclarer une variable globale

7 réponses
Avatar
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

7 réponses

Avatar
Bruno Desthuilliers
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.
Avatar
Fabrice Delente
Bruno Desthuilliers wrote:
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
Avatar
Bruno Desthuilliers
Fabrice Delente a écrit :
Bruno Desthuilliers wrote:
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.
Avatar
Bruno Desthuilliers
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)
Avatar
Fabrice Delente
Bruno Desthuilliers wrote:
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
Avatar
Bruno Desthuilliers
Fabrice Delente a écrit :
Bruno Desthuilliers wrote:
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.
Avatar
Fabrice Delente
Bruno Desthuilliers wrote:
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