Entre local et global

Le
ll.snark
Bonjour,

Y a t il un équivalent du mot clé global, mais qui désigne une port=
é
plus petite (juste la portés ENGLOBANTE) ?

Voici un exemple :

def fonc1() :
lst=[]
def fonca(v) :
if len(lst)<3 :
lst.append(v)
print(lst)
return fonca

Puis dans le shell, je fais :
>>> f1=fonc1()
>>> f1(1)
[1]
>>> f1(2)
[1, 2]
>>> f1(3)
[1, 2, 3]
>>> f1(4)
[1, 2, 3]

Tout marche comme prévu, la liste grandit à concurrence de 3 valeurs.

Maintenant, je remplace lst.append(v) par lst=lst+[v], qui crée donc
une nouvelle liste, qui va être référencée par lst.

SI j'écris :

def fonc1() :
lst=[]
def fonca(v) :
if len(lst)<3 :
lst=lst+[v]
print(lst)
return fonca

alors ça ne marche plus :
>>> f1=fonc1()
>>> f1(1)
Traceback (most recent call last):
File "<pyshell#202>", line 1, in <module>
f1(1)
File "tmp.py", line 4, in fonca
if len(lst)<3 :
UnboundLocalError: local variable 'lst' referenced before assignment

J'ai mis un peu de temps à piger pourquoi l'erreur était sur cette
ligne. J'imagine que Python lit **en avance** le code de fonca, voit
l'affectation, lst=lst+[v] et décide donc que lst est une variable
locale (je suis pas super satisfait de mon explication. si
quelqu'un en a une autre, je prends)
Mais puisque maintenant c'est une variable locale à fonca, on ne peut
plus faire :
if len(lst)<3
puisqu'à ce moment là, lst n,'existe pas.

J'aimerais donc pouvoir indiquer dans fonca, que la variable lst est
celle définie dans fonc1.
Je ne veux pas d'une variable locale à fonca, ni une variable globale
à tout mon fichier (cf exemple ci-dessous)

Une idée ?

Le code suivant par exemple ;
lst=[]
def fonc1() :
def fonca(v) :
global lst
if len(lst)<3 :
lst=lst+[v]
print(lst)
return fonca

marche comme je veux. Mais là, lst est globale. Je voudrais qu'elle
reste locale à fonc1.

Par avance merci.
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Laurent Claessens
Le #23910031
J'aimerais donc pouvoir indiquer dans fonca, que la variable lst est
celle définie dans fonc1.
Je ne veux pas d'une variable locale à fonca, ni une variable globale
à tout mon fichier (cf exemple ci-dessous)

Une idée ?



Je ne suis pas très sûr que ça réponse à la question, mais une piste
serait de faire de fonca une classe avec une méthode __call__ et une
variable de classe:

class fonc2(object):
fonc1.lst=[]
def __call__(self,v):
if len(fonc1.lst)<3:
fonc1.lst=fonc1.lst+[v]
print fonc1.lst

g=fonc2()
g(3)
g(4)
h=fonc2()
h(5)
h(6)

Retourne ce qu'il faut, si j'ai bien compris :

[3]
[3, 4]
[3, 4, 5]
[3, 4, 5]


Explications.

1. dans une classe, la méthode __call__ est celle appelée si on utilise
une instance comme une fonction. Si foo est une instance de MaClasse, faire
foo(4)
revient à faire
foo.__call__(4)

2. lorsque je définit à l'intérieur de MaClasse des variables par
MaClasse.foo = ...
alors je crée une variable de classe. Elle aura la même valeur quel que
soit l'instance. Et d'ailleurs, ce n'est pas une variable qui peut être
appelée via une instance.
MaClasse.foo
peut être vu comme un attribut de l'instance "MaClasse" de la classe
"class" :):)

Bonne aprème
Laurent
Brice V.
Le #23910491
On Thu, 27 Oct 2011 07:45:47 -0700, ll.snark wrote:

Bonjour,

Y a t il un équivalent du mot clé global, mais qui désigne une porté
plus petite (juste la portés ENGLOBANTE) ?

Voici un exemple :

def fonc1() :
lst=[]
def fonca(v) :
if len(lst)<3 :
lst.append(v)
print(lst)
return fonca

Puis dans le shell, je fais :
f1=fonc1()
f1(1)






[1]
f1(2)






[1, 2]
f1(3)






[1, 2, 3]
f1(4)






[1, 2, 3]

Tout marche comme prévu, la liste grandit à concurrence de 3 valeurs.

Maintenant, je remplace lst.append(v) par lst=lst+[v], qui crée donc une
nouvelle liste, qui va être référencée par lst.

SI j'écris :

def fonc1() :
lst=[]
def fonca(v) :
if len(lst)<3 :
lst=lst+[v]
print(lst)
return fonca

alors ça ne marche plus :
f1=fonc1()
f1(1)






Traceback (most recent call last):
File "<pyshell#202>", line 1, in <module>
f1(1)
File "tmp.py", line 4, in fonca
if len(lst)<3 :
UnboundLocalError: local variable 'lst' referenced before assignment

J'ai mis un peu de temps à piger pourquoi l'erreur était sur cette
ligne. J'imagine que Python lit **en avance** le code de fonca, voit
l'affectation, lst=lst+[v] et décide donc que lst est une variable
locale (je suis pas super satisfait de mon explication.... si quelqu'un
en a une autre, je prends)
Mais puisque maintenant c'est une variable locale à fonca, on ne peut
plus faire :
if len(lst)<3
puisqu'à ce moment là, lst n,'existe pas.

J'aimerais donc pouvoir indiquer dans fonca, que la variable lst est
celle définie dans fonc1.
Je ne veux pas d'une variable locale à fonca, ni une variable globale à
tout mon fichier (cf exemple ci-dessous)

Une idée ?

Le code suivant par exemple ;
lst=[]
def fonc1() :
def fonca(v) :
global lst if len(lst)<3 :
lst=lst+[v]
print(lst)
return fonca

marche comme je veux. Mais là, lst est globale. Je voudrais qu'elle
reste locale à fonc1.

Par avance merci.



Salut,

Je pense que tu devrais faire un lst.append(v) ou un lst.extend(v) -- ça
dépend du type de v -- à la place lst=lst+[v].

Brice
ll.snark
Le #23912201
On 27 oct, 19:08, "Brice V."
On Thu, 27 Oct 2011 07:45:47 -0700, ll.snark wrote:
> Bonjour,

> Y a t il un équivalent du mot clé global, mais qui désigne une po rté
> plus petite (juste la portés ENGLOBANTE) ?

> Voici un exemple :

> def fonc1() :
>     lst=[]
>     def fonca(v) :
>         if len(lst)<3 :
>             lst.append(v)
>         print(lst)
>     return fonca

> Puis dans le shell, je fais :
>>>> f1=fonc1()
>>>> f1(1)
> [1]
>>>> f1(2)
> [1, 2]
>>>> f1(3)
> [1, 2, 3]
>>>> f1(4)
> [1, 2, 3]

> Tout marche comme prévu, la liste grandit à concurrence de 3 valeur s.

> Maintenant, je remplace lst.append(v) par lst=lst+[v], qui crée don c une
> nouvelle liste, qui va être référencée par lst.

> SI j'écris :

> def fonc1() :
>     lst=[]
>     def fonca(v) :
>         if len(lst)<3 :
>             lst=lst+[v]
>         print(lst)
>     return fonca

> alors ça ne marche plus :
>>>> f1=fonc1()
>>>> f1(1)
> Traceback (most recent call last):
>   File "<pyshell#202>", line 1, in <module>
>     f1(1)
>   File "tmp.py", line 4, in fonca
>     if len(lst)<3 :
> UnboundLocalError: local variable 'lst' referenced before assignment

> J'ai mis un peu de temps à piger pourquoi l'erreur était sur cette
> ligne. J'imagine que Python lit **en avance** le code de fonca, voit
> l'affectation, lst=lst+[v] et décide donc que lst est une variable
> locale (je suis pas super satisfait de mon explication.... si quelqu'un
> en a une autre, je prends)
> Mais puisque maintenant c'est une variable locale à fonca, on ne peut
> plus faire :
>         if len(lst)<3
> puisqu'à ce moment là, lst n,'existe pas.

> J'aimerais donc pouvoir indiquer dans fonca, que la variable lst est
> celle définie dans fonc1.
> Je ne veux pas d'une variable locale à fonca, ni une variable globale à
> tout mon fichier (cf exemple ci-dessous)

> Une idée ?

> Le code suivant par exemple ;
> lst=[]
> def fonc1() :
>     def fonca(v) :
>         global lst if len(lst)<3 :
>             lst=lst+[v]
>         print(lst)
>     return fonca

> marche comme je veux. Mais là, lst est globale. Je voudrais qu'elle
> reste locale à fonc1.

> Par avance merci.

Salut,

Je pense que tu devrais faire un lst.append(v) ou un lst.extend(v) -- ç a
dépend du type de v -- à la place lst=lst+[v].

Brice




C'est ce que j'ai mis dans le premier code proposé.
Je voudrais justement comprendre comment faire **sans ça**, c'est à
dire en **affectant** réellement
la variable lst plutôt qu'en **modifiant** l'objet.
Il s'agit plus de comprendre comment ça marche que de parvenir à
réaliser un prog particulier (l'exemple que je donne vient d'un
exemple sur les décorateurs que je n'avais pas bien compris, et que
j'ai simplifié, jusqu'à m'apercevoir du "pb" mentionné dans mon mail.

L.
Alain Ketterlin
Le #23912381
"ll.snark"
[...]
def fonc1() :
lst=[]
def fonca(v) :
if len(lst)<3 :
lst.append(v)
print(lst)
return fonca



[...]

SI j'écris :

def fonc1() :
lst=[]
def fonca(v) :
if len(lst)<3 :
lst=lst+[v]
print(lst)
return fonca

alors ça ne marche plus :
f1=fonc1()
f1(1)






Traceback (most recent call last):
File "<pyshell#202>", line 1, in <module>
f1(1)
File "tmp.py", line 4, in fonca
if len(lst)<3 :
UnboundLocalError: local variable 'lst' referenced before assignment

J'ai mis un peu de temps à piger pourquoi l'erreur était sur ce tte
ligne. J'imagine que Python lit **en avance** le code de fonca, voit
l'affectation, lst=lst+[v] et décide donc que lst est une variable
locale (je suis pas super satisfait de mon explication.... si
quelqu'un en a une autre, je prends)



C'est exactement cela : si il y a une affectation à la variable dans la
fonction, elle est considérée locale (même si il y a un acc ès à sa
valeur avant l'affectation).

Au passage : bien sûr que python lit "en avance", il n'exécute pa s le
texte directement. Tous les noms qu'il voit dans une fonction sont soit
locaux (et accédés rapidement via un tableau) soit globaux (et ac cédés
lentement via un dictionnaire). Il doit décider de cela au moment oà ¹ il
voit la fonction, pas au moment où elle est appelée. Le modè le de portée
de python n'est pas plus compliqué que cela, il est à moitié fini, c'est
pour cela que tu as ce problème.

Mais puisque maintenant c'est une variable locale à fonca, on ne peut
plus faire :
if len(lst)<3
puisqu'à ce moment là, lst n,'existe pas.



Bon, tu pourrais faire mylst = lst avant la boucle et utiliser ensuite
mylst, mais... tu reviens au problème ci-dessus puisque mylst
deviendrait locale à fonca (donc les valeurs ne s'accumulerait pas).

J'aimerais donc pouvoir indiquer dans fonca, que la variable lst est
celle définie dans fonc1.



Qui est une variable locale dans fonc1, donc qui cesse d'exister après
l'appel à fonc1 (on parle bien de la variable, pas de l'objet
référencé).

Je ne veux pas d'une variable locale à fonca, ni une variable globale
à tout mon fichier (cf exemple ci-dessous)



Il n'y a pas en python l'équivalent du static de C/C++ (une variable
globale à portée restreinte).

En fait, tu veux un objet qui conserve un état. La solution de Laurent
est à mon avis la meilleure (ça revient à peu près à   conserver une
variable globale, mais en plus propre). Ou alors tu veux un génér ateur
peut-être, ça dépend de ton problème exact.

-- Alain.
Laurent Claessens
Le #23912591


Effectivement, ça répond en partie à la question.
Il faudra que je vois si en faisant comme ça, je peux résoudre mon pb
de départ sur les décorateurs. MAis je suppose qu'entre une fonction
et une classe qui possède une
méthode __call__, il n'y a pas tant de différence que ça...




Moi je dirais que oui, il y a une différence énorme : dans une classe tu
peux mettre beaucoup plus de choses ;)

class Exemple(object):
def __init__(self,a):
self.a=a
def __call__(self,x):
return a+x

f=Exemple(2)
g=Exemple(10)

f(1) # 3
g(1) # 11
f.a=6
f(1) # 7

Faire ça avec une fonction, ça me semblerait ardu, sauf à faire comme tu
faisais : une fonction qui contient une fonction et qui la retourne.
(mais à mon avis c'est moins lisible)

Merci pour la réponse en tous cas. Je suppose donc qu'il n'y a pas de
mot clé, un peu comme global, mais qui
désigne une portée plus petite ?



Je ne sais pas.


Je l'aurais sans doute vu dans la doc
ou dans des bouquin si ça avait été le cas, mais je trouve
que c'est bizarre.



A priori, ça ne me semble pas tellement bizarre. J'utiliserais une
fonction seulement pour quelque chose qui a une entrée et une sortie qui
dépend uniquement de l'entrée.
Pour quelque chose dont le comportement dépend du contexte (c'est le cas
de ce que tu demandes), j'utiliserais une classe dans les attributs de
laquelle je mettrais les éléments du contexte.
Bon, je dis ça, mais je ne suis pas assez fort pour prétendre être
certain de ce qui est "bizarre" ou non :)

Bonne journée
Laurent
Laurent Claessens
Le #23912581
Le 28/10/2011 11:34, Alain Ketterlin a écrit :
"ll.snark"
[...]
def fonc1() :
lst=[]
def fonca(v) :
if len(lst)<3 :
lst.append(v)
print(lst)
return fonca

[...]

SI j'écris :

def fonc1() :
lst=[]
def fonca(v) :
if len(lst)<3 :
lst=lst+[v]
print(lst)
return fonca

alors ça ne marche plus :
f1=fonc1()
f1(1)
Traceback (most recent call last):
File "<pyshell#202>", line 1, in<module>
f1(1)
File "tmp.py", line 4, in fonca
if len(lst)<3 :
UnboundLocalError: local variable 'lst' referenced before assignment

J'ai mis un peu de temps à piger pourquoi l'erreur était sur cette
ligne. J'imagine que Python lit **en avance** le code de fonca, voit
l'affectation, lst=lst+[v] et décide donc que lst est une variable
locale (je suis pas super satisfait de mon explication.... si
quelqu'un en a une autre, je prends)

C'est exactement cela : si il y a une affectation à la variable dans la
fonction, elle est considérée locale (même si il y a un accès à sa
valeur avant l'affectation).

Au passage : bien sûr que python lit "en avance", il n'exécute pas le
texte directement. Tous les noms qu'il voit dans une fonction sont soit
locaux (et accédés rapidement via un tableau) soit globaux (et accédés
lentement via un dictionnaire). Il doit décider de cela au moment où il
voit la fonction, pas au moment où elle est appelée. Le modèle de portée
de python n'est pas plus compliqué que cela, il est à moitié fini, c'est
pour cela que tu as ce problème.

Mais puisque maintenant c'est une variable locale à fonca, on ne peut
plus faire :
if len(lst)<3
puisqu'à ce moment là, lst n,'existe pas.

Bon, tu pourrais faire mylst = lst avant la boucle et utiliser ensuite
mylst, mais... tu reviens au problème ci-dessus puisque mylst
deviendrait locale à fonca (donc les valeurs ne s'accumulerait pas).

J'aimerais donc pouvoir indiquer dans fonca, que la variable lst est
celle définie dans fonc1.

Qui est une variable locale dans fonc1, donc qui cesse d'exister après
l'appel à fonc1 (on parle bien de la variable, pas de l'objet
référencé).

Je ne veux pas d'une variable locale à fonca, ni une variable globale
à tout mon fichier (cf exemple ci-dessous)

Il n'y a pas en python l'équivalent du static de C/C++ (une variable
globale à portée restreinte).

En fait, tu veux un objet qui conserve un état. La solution de Laurent
est à mon avis la meilleure (ça revient à peu près à conserver une
variable globale, mais en plus propre). Ou alors tu veux un générateur
peut-être, ça dépend de ton problème exact.

-- Alain.
Pierre Maurette
Le #23912791
ll.snark :

[...]

C'est ce que j'ai mis dans le premier code proposé.
Je voudrais justement comprendre comment faire **sans ça**, c'est à
dire en **affectant** réellement
la variable lst plutôt qu'en **modifiant** l'objet.



Changer de langage ?

Il s'agit plus de comprendre comment ça marche que de parvenir à
réaliser un prog particulier (l'exemple que je donne vient d'un
exemple sur les décorateurs que je n'avais pas bien compris, et que
j'ai simplifié, jusqu'à m'apercevoir du "pb" mentionné dans mon mail.



<code>
def fonc1() :
lst = [[],]
def fonca(v) :
if len(lst[0]) < 3 :
lst[0] = lst[0] + [v]
print lst[0]
return fonca

f1 = fonc1()
[f1(i+1) for i in range(4)]
</code>

Il me semble effectivement *très important* de bien comprendre les
différences entre lst.append(v), lst=lst+[v] et lst[0] = lst[0] + [v],
même si on n'est qu'utilisateur de Python sans autre prétention que
l'efficacité. C'est souvent à l'envers qu'on découvrira ces
différences, à ses dépends. Bien comprendre, dans ce cas, c'est prévoir
la réponse sans hésitation, ce qui bien entendu devrait être *toujours*
le cas, mais bon... Passer un peu de temps avec des print, print id()
ou même un debugger (/Debug perspective/ dans Eclipse - Pydev, c'est
tout confort) n'est pas un mauvais investissement.

--
Pierre Maurette
Alain Ketterlin
Le #23912861
Laurent Claessens
Le 28/10/2011 11:34, Alain Ketterlin a écrit :



[...]

J'ai beau chercher, je ne trouve pas ce que contient ton message, sinon
une citation difficile à déchiffrer du mien.

-- Alain.
Pierre Maurette
Le #23912901
Alain Ketterlin :
Laurent Claessens
Le 28/10/2011 11:34, Alain Ketterlin a écrit :



[...]

J'ai beau chercher, je ne trouve pas ce que contient ton message, sinon
une citation difficile à déchiffrer du mien.



Un "+1" *très* lapidaire ?

--
Pierre Maurette
ll.snark
Le #23928181
Bonjour,

Merci pour vos réponses.

Je récapitule ici les 3 solutions "fonctionnelles" (bien qu'on ne voit
pas trop à quoi elles peuvent servir excepté alimenter des
discussions...) :
Toutes doivent afficher :

[1]
[1,2]
[1,2,3]
[1,2,3]

Solution 1)
-----------------
def fonc1() :
lst=[]
def fonca(v) :
if len(lst)<3 :
lst.append(v)
print(lst)
return fonca
f1 = fonc1()
[f1(i+1) for i in range(4)]


Solution 2)
-----------------
(Code de Laurent, modifié, car il y a eu une confusion, je crois entre
fonc1 et fonc2...
Au passage cette confusion présumée provoque un comportement bizarre.
Si je me trompe, merci à Laurent de m'envoyer un message privé, et je
rectifierai ici).

class fonc2(object):
lst=[]
def __call__(self,v):
if len(fonc2.lst)<3:
fonc2.lst=fonc2.lst+[v]
print(fonc2.lst)

f1=fonc2()
[f1(i+1) for i in range(4)]

Solution 3)
-----------------
def fonc1() :
lst = [[],]
def fonca(v) :
if len(lst[0]) < 3 :
lst[0] = lst[0] + [v]
print(lst[0])
return fonca

f1 = fonc1()
[f1(i+1) for i in range(4)]


Enfin, ceci ne marche évidemment pas :
------------------
def fonc1() :
lst=[]
def fonca(v) :
if len(lst)<3 :
lst=lst+[v]
print(lst)
return fonca
Publicité
Poster une réponse
Anonyme