OVH Cloud OVH Cloud

probleme de variables locales et globales...

10 réponses
Avatar
N. Pourcelot
Bonjour,
je me prend la tête ce matin sur le problème suivant :
Je cree une classe, disons A.

class A:
def __init__(self):
self.n = 3
self.m = None
def h(self, ini):
n = self.n
m = self.m
if ini: exec("def m(x): return n+x"); self.m=m
else: m(7)

Maintenant, je lance :
obj = A()
obj.h(1)
obj.h(0)

j'obtiens :

Traceback (most recent call last):
File "<input>", line 1, in ?
File "<input>", line 9, in h
File "<string>", line 1, in m
NameError: global name 'n' is not defined

Maintenant, supposons que je supprime l'instruction exec.

class A:
def __init__(self):
self.n = 3
self.m = None
def h(self, ini):
n = self.n
m = self.m
if ini:
def m(x): return n+x
self.m=m
else: return m(7)

je lance :
obj = A()
obj.h(1)
obj.h(0)

Cette fois, pas d'erreur !

Bon, bien sûr, présenté comme ça, ce problème n'a pas d'intérêt, mais
dans mon vrai programme, je suis quasi-obligé d'utiliser une instruction
exec, et je ne vois pas d'où vient l'erreur dans le premier cas...?!?

Merci d'avance ! :-)

10 réponses

Avatar
kaerbuhez


Bon, bien sûr, présenté comme ça, ce problème n'a pas d'intérêt, mais
dans mon vrai programme, je suis quasi-obligé d'utiliser une instruction
exec, et je ne vois pas d'où vient l'erreur dans le premier cas...?!?



La vraie discution interessante serait sur le "quasi-obligé" mais bon,
je me suis amusé (honte sur moi) à résoudre ton probléme tel que tu le
poses. Ceci fonctionne (mais je n'en suis pas trés fier ;-) ):

class A:
def __init__(self):
self.n = 3
self.m = None
def h(self, ini):
n = self.n
m = self.m
if ini:
exec("""
def m(n):
def mm(x): return n+x
return mm""")
self.m=m(n)
else: return m(7)

obj = A()
print obj.h(1)
print obj.h(0)

Avatar
N. Pourcelot
Merci pour ta solution !
Malheureusement, elle ne peut pas me convenir...

1. J'ai pris f(x)=x+n au hasard, mais en fait la fonction g(x,n)qui
sous-tend la fonction f(x) n'est pas forcément symétrique (et je ne la
connais pas à l'avance).

2. Dans ton exemple, n est fixé une fois pour toute.
En fait, je veux que n = self.n a tout moment.



Pour plus d'info, j'utilise ce genre de choses dans une calculatrice.
L'utilisateur rentre des choses du style :
t=5
f(x,y,a)=2x+y+3a+5+t
f(7)


J'éxecute alors, entre autres :
def f(x,y,a) = 2x+y+3a+5+t
self.f = f



kaerbuhez wrote:


Bon, bien sûr, présenté comme ça, ce problème n'a pas d'intérêt, mais
dans mon vrai programme, je suis quasi-obligé d'utiliser une
instruction exec, et je ne vois pas d'où vient l'erreur dans le
premier cas...?!?



La vraie discution interessante serait sur le "quasi-obligé" mais bon,
je me suis amusé (honte sur moi) à résoudre ton probléme tel que tu le
poses. Ceci fonctionne (mais je n'en suis pas trés fier ;-) ):

class A:
def __init__(self):
self.n = 3
self.m = None
def h(self, ini):
n = self.n
m = self.m
if ini:
exec("""
def m(n):
def mm(x): return n+x
return mm""")
self.m=m(n)
else: return m(7)

obj = A()
print obj.h(1)
print obj.h(0)




Avatar
N. Pourcelot
Personne n'a idée d'où vient le problème ?
A défaut d'une solution, si je savais pourquoi le premier exemple
marche, et pas le deuxième, ça me donnerait une idée sur la manière de
contourner le problème...

N. Pourcelot wrote:
Bonjour,
je me prend la tête ce matin sur le problème suivant :
Je cree une classe, disons A.

class A:
def __init__(self):
self.n = 3
self.m = None
def h(self, ini):
n = self.n
m = self.m
if ini: exec("def m(x): return n+x"); self.m=m
else: m(7)

Maintenant, je lance :
obj = A()
obj.h(1)
obj.h(0)

j'obtiens :

Traceback (most recent call last):
File "<input>", line 1, in ?
File "<input>", line 9, in h
File "<string>", line 1, in m
NameError: global name 'n' is not defined

Maintenant, supposons que je supprime l'instruction exec.

class A:
def __init__(self):
self.n = 3
self.m = None
def h(self, ini):
n = self.n
m = self.m
if ini:
def m(x): return n+x
self.m=m
else: return m(7)

je lance :
obj = A()
obj.h(1)
obj.h(0)

Cette fois, pas d'erreur !

Bon, bien sûr, présenté comme ça, ce problème n'a pas d'intérêt, mais
dans mon vrai programme, je suis quasi-obligé d'utiliser une instruction
exec, et je ne vois pas d'où vient l'erreur dans le premier cas...?!?

Merci d'avance ! :-)


Avatar
N. Pourcelot
J'avance un peu...
Si j'ai bien compris, les dictionnaires globals et locals attachés au
exec de ma fonction h le sont une fois pour toute, lors de la creation
de la fonction ?
Il faut donc que je passe a chaque execution le nouveau dico ?!?

N. Pourcelot wrote:
Bonjour,
je me prend la tête ce matin sur le problème suivant :
Je cree une classe, disons A.

class A:
def __init__(self):
self.n = 3
self.m = None
def h(self, ini):
n = self.n
m = self.m
if ini: exec("def m(x): return n+x"); self.m=m
else: m(7)

Maintenant, je lance :
obj = A()
obj.h(1)
obj.h(0)

j'obtiens :

Traceback (most recent call last):
File "<input>", line 1, in ?
File "<input>", line 9, in h
File "<string>", line 1, in m
NameError: global name 'n' is not defined

Maintenant, supposons que je supprime l'instruction exec.

class A:
def __init__(self):
self.n = 3
self.m = None
def h(self, ini):
n = self.n
m = self.m
if ini:
def m(x): return n+x
self.m=m
else: return m(7)

je lance :
obj = A()
obj.h(1)
obj.h(0)

Cette fois, pas d'erreur !

Bon, bien sûr, présenté comme ça, ce problème n'a pas d'intérêt, mais
dans mon vrai programme, je suis quasi-obligé d'utiliser une instruction
exec, et je ne vois pas d'où vient l'erreur dans le premier cas...?!?

Merci d'avance ! :-)


Avatar
N. Pourcelot
Bon, je me répond à moi-même, dès fois que ça puisse servir à qqn ?
Je crois que j'ai compris :

Les 5 lignes de code ci-dessous génèrent une erreur :

def f(x):
a=4
def g(x): eval("x+a")
g(5)
f(3)

Traceback (most recent call last):
File "<input>", line 1, in ?
File "<input>", line 5, in f
File "<input>", line 4, in g
File "<string>", line 0, in ?
NameError: name 'a' is not defined

La fonction fille n'importe pas toutes le variables locales de la
fonctions mères, mais seulement celles qui vont servir.
Donc le a n'est pas détécté.
Je suppose que c'est pour des raisons d'optimisation ??
En tout cas, ça conduit à des résultats très surprenants...

Bon, il reste plus qu'à régler ça à coup de locals()
Avatar
Do Re Mi chel La Si Do
Bonsoir !

class A:
def __init__(self):
self.n = 3
self.m = None
def h(self, ini):
global n #********ligne_ajoutée*********
n = self.n
m = self.m
if ini: exec("def m(x): return n+x"); self.m=m
else: m(7)

obj = A()
obj.h(1)
obj.h(0)
Avatar
N. Pourcelot
C'est une solution en effet, mais je n'aime pas trop utiliser des
variables globales dans un (assez) gros programme...
Pour l'instant, je me suis contenté de rajouter un locals() au exec,
mais le n est évalué une fois pour toute (après tout, ce n'est pas moins
intuitif pour l'utilisateur final).

Do Re Mi chel La Si Do wrote:
Bonsoir !

class A:
def __init__(self):
self.n = 3
self.m = None
def h(self, ini):
global n #********ligne_ajoutée*********
n = self.n
m = self.m
if ini: exec("def m(x): return n+x"); self.m=m
else: m(7)

obj = A()
obj.h(1)
obj.h(0)




Avatar
Amaury
J'avance un peu...
Si j'ai bien compris, les dictionnaires globals et locals attachés au
exec de ma fonction h le sont une fois pour toute, lors de la creation
de la fonction ?
Il faut donc que je passe a chaque execution le nouveau dico ?!?



Je n'ai pas le temps de regarder, mais il me semble que l'on peut
indiquer un dictionnaire à exec, du genre
exec "def m(x): return n+x" in locals()

Autre question: pourquoi un 'exec' ?
l'instruction def est parfaitement autorisée dans le corps d'une fonction...
Ou alors j'ai manqué quelque chose ?

--
Amaury

Avatar
Do Re Mi chel La Si Do
Bonsoir !

il me semble que l'on peut indiquer un dictionnaire à exec




On peut même indiquer deux dictionnaires :
exec("def m(x): return n+x", globals(), locals()


On peut aussi précompiler :
ccod=compile('def m(x): return n+x','Class.A','exec')
et, pour l'utiliser :
exec(ccod,globals(),globals()) #autre utilisation des dicos

Avantage, on peut tester la syntaxe, indépendamment de l'utilisation, et
cela évite de recompiler n'importe quand.



@-salutations

Michel Claveau



Avatar
N. Pourcelot
Amaury wrote:

Autre question: pourquoi un 'exec' ?
l'instruction def est parfaitement autorisée dans le corps d'une
fonction...
Ou alors j'ai manqué quelque chose ?

--
Amaury


Pour plus d'info, j'utilise ce genre de choses dans une calculatrice.
L'utilisateur rentre des choses du style :
t=5
f(x,y,a)=2x+y+3a+5+t
f(7)


J'éxecute alors, entre autres :
def f(x,y,a) = 2x+y+3a+5+t
self.f = f