OVH Cloud OVH Cloud

Passages de paramètres

39 réponses
Avatar
Guillaume Bouchard
Bonjour/soir.

J'ai débuté le python sérieusement il y a de cela quelques mois et il me
sert principalement pour faire des scripts d'administrations. (Pour la
petite histoire, je faisais mes script en php avant, mais l'admin de mon
école à enlever le support php pour la ligne de commande, j'ai du changé
de langage et j'ai découvert avec plaisir le python, qui si il ne
remplacera jamais le php pour le web, est devenu mon language de
prédilection en matière de scripts et petits programmes.)

Cependant je compte me lancer dans quelque chose de plus gros (mon rève
a toujours été de faire un jeu) et pour cela j'ai besoin de faire le
point sur certains comportements qui ne m'on jamais embétés sur des
scripts de 20 lignes mais qui risques de peser lourd par la suite. Plus
précisement, je prefere me lancer dans un gros dev en sachant ce que va
faire ce que j'écris et non pas comme ce qui se passe actuellement ou je
test et je vois ce qui se passe. Pour l'exemple j'ai codé une fonction
récursive de gestion de graph dernièrement et je ne comprand pas comment
elle fonctionne, mais elle fonctionne...

Avant toute chose, je tient à signaler que mes attentes vis à vis d'un
comportement sont fortement influencées par les langages que je
maitrises, Php principalement. Je suis pret à changer "d'attentes" à la
seule condition que je comprenne la philosophie du problème.

1) J'ai quelques problèmes pour comprandre le systeme de "copie" de
variables, exemple:

>>> a = 0; b = a; b = 2; (a,b)
(0, 2)
>>> a = "a"; b = a ; b = "b"; (a,b)
('a', 'b')
>>> a = (1,); b = a; b = (2,); (a,b)
((1,), (2,))
>>> a = [0]; b = a;b = [1]; a,b
([0], [1])
>>> a = [0]; b = a;b[0] = 1; a,b
([1], [1]) # Ici ce résultat me choque...

D'un autre coté, cela peu sembler logique, dans tous les autres cas je
réaffecte b, alors que là je ne fait que modifier un élément de b, donc.

Comment gérer-vous ce comportement bizarre (à mon sens, je doute qu'il
soit bizarre pour vous, mais alors expliquer moi pourquoi il n'est pas
bizarre...)

2) Un autre exemple qui me rend dingue :

def double(i):
i *= 2
return i
a = 1
print double(a) # 2 attendu OK
print a # 1 attendu OK

a = [1]
print double(a) # [1,1] OK
print a # Je m'attendais à [1], j'ai [1,1]

# ---
def double2(i):
i = i * 2
return i

a = [1]
print double2(a) # [1,1] OK
print a # Correspond à mon attente,
# mais pas au test précedent.


Pourquoi donc les operateurs ne reagissent pas de la même façon alors
qu'ils sont sensés être synonymes ?

3) Un troisème exemple, j'ai voulu faire une fonction prenant comme
paramétre d'entrée optionel une liste :


def func(a = []): # Valeur par defaut, la liste vide
a.append(len(a))
return a


z = func()
print z # Attendu [0], OK
y = func()
print y # Attendu [0], obtenu [0,1]

b = range(3)
print func(b) # Attendu [0,1,2,3] OK
print b # Obtenu [0,1,2,3], attendu [0,1,2]

Donc première chose, apparament lorsque l'on alloue comme valeur par
defaut une liste, elle agit comme un élement "static" ? J'ai lu cela
quelque part dans la doc, je le conçoit mais j'aimerais comprandre
Pourquoi ? (Je n'arrive pas à assimiler des choses qui n'ont pas de
logique, je cherche donc la logique derrière)

Dans le cas du deuxième test, apparament la variable b est modifiée par
l'appel à la fonction func. Il s'agit d'une variable "mutable", donc
c'est un comportement normal vis à vis de la doc, mais pas vis à vis de
ce que j'attend, pourquoi ?

Comment faites vous pour faire une fonction qui prend en paramètre une
variable et renvoie une variable y ressemblant, mais modifiée. Passez
vous obligatoirement pas copy.deepcopy ? Dans le cas de liste simple on
peux facilement faire un a = b[:], mais dans le cas de listes complexes,
cela devient plus corriaces. Comment expliquez vous ce concept de copie
par réference à tout bout de champs ?

Bref, pas vraiment de question, juste besoin de comprandre la
philosophie qui se cache derrière ces comportements surprenants.

Merci si vous pouvez éclairer ma lanterne ou me rediriger vers une doc
qui le fera...

Encore désolé pour les fautes qui se cachent dans ce que j'écris.
--
Guillaume.

10 réponses

1 2 3 4
Avatar
Jerome
Guillaume Bouchard wrote:
Bonjour/soir.


1) J'ai quelques problèmes pour comprandre le systeme de "copie" de
variables, exemple:

a = 0; b = a; b = 2; (a,b)
(0, 2)



a = "a"; b = a ; b = "b"; (a,b)
('a', 'b')



a = (1,); b = a; b = (2,); (a,b)
((1,), (2,))



a = [0]; b = a;b = [1]; a,b
([0], [1])



a = [0]; b = a;b[0] = 1; a,b
([1], [1]) # Ici ce résultat me choque...





b=a ne copie pas la valeur mais la référence. En modifiant b, tu modifie
l'objet pointé également.

Comment gérer-vous ce comportement bizarre (à mon sens, je doute qu'il
soit bizarre pour vous, mais alors expliquer moi pourquoi il n'est pas
bizarre...)


a = [0]; b = a[:] ; b[0] = 1 ; a,b




2) Un autre exemple qui me rend dingue :

def double(i):
i *= 2
return i
a = 1
print double(a) # 2 attendu OK
print a # 1 attendu OK

a = [1]
print double(a) # [1,1] OK
print a # Je m'attendais à [1], j'ai [1,1]


même problème qu'avant, a est modifié dans ta fonction, tu passes un
référence


# ---
def double2(i):
i = i * 2
return i

a = [1]
print double2(a) # [1,1] OK
print a # Correspond à mon attente,
# mais pas au test précedent.


Dans ce cas là tu ne modifies pas a, tu en crées un nouveau avec i*2


Pourquoi donc les operateurs ne reagissent pas de la même façon alors
qu'ils sont sensés être synonymes ?


ils ne sont pas sensés être synonymes, tu en as la preuve ;-)


3) Un troisème exemple, j'ai voulu faire une fonction prenant comme
paramétre d'entrée optionel une liste :


def func(a = []): # Valeur par defaut, la liste vide
a.append(len(a))
return a


z = func()
print z # Attendu [0], OK
y = func()
print y # Attendu [0], obtenu [0,1]



C'est un peu plus complexe, le paramêtre par défaut [] est un objet
unique qui est réutilisé à chaque fois et donc modifié au fur et à mesure.
rajoutes un print z après ton print y pour t'en convaincre.


b = range(3)
print func(b) # Attendu [0,1,2,3] OK
print b # Obtenu [0,1,2,3], attendu [0,1,2]


Comme tout à l'heure, tu modifies a et donc b qui est passé par référence.



Donc première chose, apparament lorsque l'on alloue comme valeur par
defaut une liste, elle agit comme un élement "static" ? J'ai lu cela
quelque part dans la doc, je le conçoit mais j'aimerais comprandre
Pourquoi ? (Je n'arrive pas à assimiler des choses qui n'ont pas de
logique, je cherche donc la logique derrière)


La logique c'est que python est orienté objet.


Dans le cas du deuxième test, apparament la variable b est modifiée par
l'appel à la fonction func. Il s'agit d'une variable "mutable", donc
c'est un comportement normal vis à vis de la doc, mais pas vis à vis de
ce que j'attend, pourquoi ?


Parce que tu n'as pas fait la différence entre passage par référence et
passage par valeur. Tu passes une référence et tu la modifies, c'est
normal qu'elle change. Si tu ne veux pas, il faut faire une copie locale
et travailler dessus.


Bref, pas vraiment de question, juste besoin de comprandre la
philosophie qui se cache derrière ces comportements surprenants.

Merci si vous pouvez éclairer ma lanterne ou me rediriger vers une doc
qui le fera...


il faut que tu relises la partie sur la programmation objet du site de
python.


Encore désolé pour les fautes qui se cachent dans ce que j'écris.


comprendre c'est avec un 'e'...




Avatar
Tibi
Guillaume Bouchard wrote:

def double(i):
i *= 2
return i
a = [1]
print double(a) # [1,1] OK
print a # Je m'attendais à [1], j'ai [1,1]


C'est parceque *= n'est pas un vrai assignement mais change la variable de
gauche sur place. i *= 2 n'est pas tout à fait équivalent à i = i * 2

def func(a = []): # Valeur par defaut, la liste vide
a.append(len(a))
return a

z = func()
print z # Attendu [0], OK
y = func()
print y # Attendu [0], obtenu [0,1]


C'est un grand classique des "gotchas" Python, la solution est simple:
éviter les listes en tant que valeur de param par défaut.

Comment faites vous pour faire une fonction qui prend en paramètre une
variable et renvoie une variable y ressemblant, mais modifiée. Passez
vous obligatoirement pas copy.deepcopy ? Dans le cas de liste simple on


Oui, si ce que tu veux est effectivement retourner une copie "en profondeur"
légèrement modifiée.

Comment expliquez vous ce concept de copie
par réference à tout bout de champs ?


C'est un concept de base du langage, c'est tout. En Java c'est pareil. En
pratique on a pas souvent besoin de faire des copies.

Bref, pas vraiment de question, juste besoin de comprandre la
philosophie qui se cache derrière ces comportements surprenants.


Désolé, je peux pas te répondre sur la philosophie... j'utilise python car
il permet d'avoir vite qqc qui tourne, sans trop me poser de questions.

Avatar
Guillaume Bouchard
Jerome wrote:
a = [0]; b = a[:] ; b[0] = 1 ; a,b





C'est déplaisant comme technique, mais soit. (soite ?)

def double(i):
i *= 2
return i
a = 1
print double(a) # 2 attendu OK
print a # 1 attendu OK

a = [1]
print double(a) # [1,1] OK
print a # Je m'attendais à [1], j'ai [1,1]



même problème qu'avant, a est modifié dans ta fonction, tu passes un
référence


Mais dans ce cas pourquoi a, (le a = 1) n'est pas modifié bien qu'aussi
passé par réference. Reponse, parce que c'est un entier, de type
immutable, donc c'est normal. Cela je comprend, mais la raison de ce
schmilbick monstreux ?


def double2(i):
i = i * 2
return i

a = [1]
print double2(a) # [1,1] OK
print a # Correspond à mon attente,
# mais pas au test précedent.



Dans ce cas là tu ne modifies pas a, tu en crées un nouveau avec i*2


Ok :

a = [0]
i = double(a)
i
[0, 0]



a[0] = 27
i
[0, 0]



a = [[0]]
i = double(a)
i
[[0], [0]]



a[0][0] = 27
i
[[27], [27]]




Faut y penser à chaques instants, bon, passons, je m'y ferais (si
quelqu'un me trouve une explication logique par pitié)

ils ne sont pas sensés être synonymes, tu en as la preuve ;-)


Le doute méthodique, oublié tout ce que j'ai apprit, un cercle est un
carré et inversement. Bref, i = i * 2 <> i *= 2... À médité.


3) Un troisème exemple, j'ai voulu faire une fonction prenant comme
paramétre d'entrée optionel une liste :


C'est un peu plus complexe, le paramêtre par défaut [] est un objet
unique qui est réutilisé à chaque fois et donc modifié au fur et à mesure.
rajoutes un print z après ton print y pour t'en convaincre.


Convaincu et j'ai compris, merci :)

La logique c'est que python est orienté objet.


Merci pour cette explication parfaitement claire. Cela me rappel mon
prof de math qui dit "Il est évident que ceci" et qui nous explique
cepandant que le théorme de l'evidence n'existe pas.

Parce que tu n'as pas fait la différence entre passage par référence et
passage par valeur. Tu passes une référence et tu la modifies, c'est
normal qu'elle change. Si tu ne veux pas, il faut faire une copie locale
et travailler dessus.


C'ets juste cet philosophie de tout passer par réference qui me turlupine.

il faut que tu relises la partie sur la programmation objet du site de
python.


J'y cours.

comprendre c'est avec un 'e'...


Cherchons la logique la dedans, hum, non pas de logique dans la langue
française. Je rajoute un post-it sur le coté de mon ecran.

Merci.

--
Guillaume.




Avatar
Guillaume Bouchard
Tibi wrote:
C'est parceque *= n'est pas un vrai assignement mais change la variable de
gauche sur place. i *= 2 n'est pas tout à fait équivalent à i = i * 2


ok.

C'est un grand classique des "gotchas" Python, la solution est simple:
éviter les listes en tant que valeur de param par défaut.


Ok, je note et j'ai compris ce comportement grace à Jérome.

C'est un concept de base du langage, c'est tout. En Java c'est pareil. En
pratique on a pas souvent besoin de faire des copies.


Ok.

Désolé, je peux pas te répondre sur la philosophie... j'utilise python car
il permet d'avoir vite qqc qui tourne, sans trop me poser de questions.


Mon problème étant que je suis incapable d'utiliser quelque chose sans
comprandre son fonctionnement (dans une certaine limite, je n'irais pas
voir le source C de python, quoi que ;))

Disont que lors de mon prochain appel de fonction, je veux être certain
que le résultat sera celui que j'attend, sans effets de bords. Donc j'ai
besoin de comprandre comment cela fonctionne.

Merci.

--
Guillaume.

Avatar
Guillaume Bouchard
Jerome wrote:
C'est juste une habitude à prendre (à perdre ?)


Je ne tient pas à perdre mes habitudes durement obtenues dans d'autre
langage, donc ce sera "prendre".

lol, tu fais les questions et les réponses.


Le problème c'est que j'ai compris en partie mes problèmes, sauf que je
cherche à comprendre d'ou ils viennent pour aquérir les automatismes.

Pour moi c'est une incohérence de python qui n'a pas une vision
totalement objet, probablement pour des raisons de performances. En
Smalltalk qui est réellement pur objet tu n'as que des références.
Je ne sais pas s'il y a une vraie raison mais tu peux voir venir un
autre problème qui est de réussir à passer un objet de type immutable en
référence...


On triche ? a = [[0]]. Op, on le passe par référence et on s'arrange
pour que cela fonctionne.

la différence c'est que dans i = i * 2, la deuxième partie de
l'expression (i * 2) créé un nouvel objet alors que dans la deuxième tu
ne passes pas par cette création mais tu modifies i.


Ok.

lol, désolé. Ce que j'ai voulu dire que Python est orienté objet et que
par conséquent il est cohérent avec les principes de bases de la
programmation objet. Si tu ne connais que des langages procéduraux ça
peut être un peu bizarre au début.


Le problème viens de là. Le seul langage orienté object que je conaisse
c'est le php. (J'entend déjà les rires au fond de la salle).

d'un point de vue étymologique comprendre signifie prendre ensemble...


C'est donc de là que viens le 'con' en espagnol qui signifie avec.
Par contre, pour trouver le rapport avec le con en français... Va faloir
se levé tôt.

Après, pour le 'e' de prendre, c'est surement du latin ;-)


Bref, y sont foux ces romains.

Merci pour tes explications, je commence à voir le bout du tunnel.

--
Guillaume.

Avatar
Jerome
Guillaume Bouchard wrote:
Jerome wrote:

a = [0]; b = a[:] ; b[0] = 1 ; a,b






C'est déplaisant comme technique, mais soit. (soite ?)


C'est juste une habitude à prendre (à perdre ?)



Mais dans ce cas pourquoi a, (le a = 1) n'est pas modifié bien qu'aussi
passé par réference. Reponse, parce que c'est un entier, de type
immutable, donc c'est normal. Cela je comprend, mais la raison de ce
schmilbick monstreux ?


lol, tu fais les questions et les réponses.
Pour moi c'est une incohérence de python qui n'a pas une vision
totalement objet, probablement pour des raisons de performances. En
Smalltalk qui est réellement pur objet tu n'as que des références.
Je ne sais pas s'il y a une vraie raison mais tu peux voir venir un
autre problème qui est de réussir à passer un objet de type immutable en
référence...


Le doute méthodique, oublié tout ce que j'ai apprit, un cercle est un
carré et inversement. Bref, i = i * 2 <> i *= 2... À médité.


la différence c'est que dans i = i * 2, la deuxième partie de
l'expression (i * 2) créé un nouvel objet alors que dans la deuxième tu
ne passes pas par cette création mais tu modifies i.


3) Un troisème exemple, j'ai voulu faire une fonction prenant comme
paramétre d'entrée optionel une liste :




C'est un peu plus complexe, le paramêtre par défaut [] est un objet
unique qui est réutilisé à chaque fois et donc modifié au fur et à
mesure.
rajoutes un print z après ton print y pour t'en convaincre.



Convaincu et j'ai compris, merci :)


cool


La logique c'est que python est orienté objet.



Merci pour cette explication parfaitement claire. Cela me rappel mon
prof de math qui dit "Il est évident que ceci" et qui nous explique
cepandant que le théorme de l'evidence n'existe pas.


lol, désolé. Ce que j'ai voulu dire que Python est orienté objet et que
par conséquent il est cohérent avec les principes de bases de la
programmation objet. Si tu ne connais que des langages procéduraux ça
peut être un peu bizarre au début.


comprendre c'est avec un 'e'...


Cherchons la logique la dedans, hum, non pas de logique dans la langue
française. Je rajoute un post-it sur le coté de mon ecran.


d'un point de vue étymologique comprendre signifie prendre ensemble...
Après, pour le 'e' de prendre, c'est surement du latin ;-)


Merci.







Avatar
Yermat
Guillaume Bouchard wrote:
Tibi wrote:

[...]
Mon problème étant que je suis incapable d'utiliser quelque chose sans

comprandre son fonctionnement (dans une certaine limite, je n'irais pas
voir le source C de python, quoi que ;))

Disont que lors de mon prochain appel de fonction, je veux être certain
que le résultat sera celui que j'attend, sans effets de bords. Donc j'ai
besoin de comprandre comment cela fonctionne.

Merci.



C'est que par défaut Python utilise des références sur les objets et ne
fait pas de copie comme PHP. Donc pour les objets "mutable"s ont utilise
des références (comme celle de PHP
http://fr3.php.net/manual/fr/language.references.php), pour les objets
"immutable"s ont utilise une copie.

Donc ton exemple sur les listes en PHP serait :
$a = array(0 => "0");
$b = & $a;
$b[0] = "1";
print_r($a);
print_r($b);

Note bien le "&" dans l'affectation...

--
Yermat


Avatar
Yermat
Jerome wrote:
Guillaume Bouchard wrote:

Jerome wrote:

[...]



lol, désolé. Ce que j'ai voulu dire que Python est orienté objet et que
par conséquent il est cohérent avec les principes de bases de la
programmation objet. Si tu ne connais que des langages procéduraux ça
peut être un peu bizarre au début.


A noter que PHP est aussi Objet maintenant mais que par défaut il fait
une copie des objets... :-(

Il faut lui demander explicitement une référence si l'on en veut une.
Par contre il y a aussi des problèmes de clonage car il ne fait pas une
copie profonde (deepcopy)...

Bref le plus simple est d'apprendre comment fonctionne un ordi en
mémoire et de comprendre que l'on peut soit référencé une zone mémoire
(pointeur C => mutable), soit copier une zone mémoire (immutable)...

Bref à trop cacher le fonctionnement d'un ordinateur on en arrive à une
mauvaise abstraction...

--
Yermat



Avatar
Jerome


A noter que PHP est aussi Objet maintenant mais que par défaut il fait
une copie des objets... :-(


Oui, j'ai entendu dire ;-) Mais ils n'ont fait que rajouter une couche
objet par dessus la couche procédurale j'imagine. Ca bloque forcément à
un moment.


Il faut lui demander explicitement une référence si l'on en veut une.
Par contre il y a aussi des problèmes de clonage car il ne fait pas une
copie profonde (deepcopy)...


J'imagine que les programmeurs php ne veulent pas vraiment de l'objet
mais juste l'encapsulation tout en gardant leurs habitudes.


Bref le plus simple est d'apprendre comment fonctionne un ordi en
mémoire et de comprendre que l'on peut soit référencé une zone mémoire
(pointeur C => mutable), soit copier une zone mémoire (immutable)...

Bref à trop cacher le fonctionnement d'un ordinateur on en arrive à une
mauvaise abstraction...


Je suis d'accord. Tant qu'on s'est pas pris la tête en c avec les
pointeurs on loupe des choses essentielles ;-)

Avatar
Eric Brunel
On Thu, 21 Apr 2005 12:30:04 +0200, Guillaume Bouchard wrote:

Bonjour/soir.

J'ai débuté le python sérieusement il y a de cela quelques mois et il me
sert principalement pour faire des scripts d'administrations. (Pour la
petite histoire, je faisais mes script en php avant, mais l'admin de mon
école à enlever le support php pour la ligne de commande, j'ai du changé
de langage et j'ai découvert avec plaisir le python, qui si il ne
remplacera jamais le php pour le web, est devenu mon language de
prédilection en matière de scripts et petits programmes.)

Cependant je compte me lancer dans quelque chose de plus gros (mon rève
a toujours été de faire un jeu) et pour cela j'ai besoin de faire le
point sur certains comportements qui ne m'on jamais embétés sur des
scripts de 20 lignes mais qui risques de peser lourd par la suite. Plus
précisement, je prefere me lancer dans un gros dev en sachant ce que va
faire ce que j'écris et non pas comme ce qui se passe actuellement ou je
test et je vois ce qui se passe. Pour l'exemple j'ai codé une fonction
récursive de gestion de graph dernièrement et je ne comprand pas comment
elle fonctionne, mais elle fonctionne...

Avant toute chose, je tient à signaler que mes attentes vis à vis d'un
comportement sont fortement influencées par les langages que je
maitrises, Php principalement. Je suis pret à changer "d'attentes" à la
seule condition que je comprenne la philosophie du problème.

[snip plein de problèmes sur le comportement des variables Python]


Les problèmes que tu as sont liés à la notion de "variable" en Python, qui n'est pas vraiment équivalente à celle qu'on peut trouver dans d'autres langages. Certains (http://minilien.com/?w3iLzmJRqF) vont meme jusqu'à dire (je cite) "en Python, y a pas de variables".

Pour une explication détaillée, voir là:
http://www.effbot.org/zone/python-objects.htm

J'en avais fait un résumé en français là:
http://minilien.com/?uOkpdcWW9B

J'espère que ça t'aidera à comprendre la "philosophie" qui se cache derrière tout ça.
--
python -c 'print "".join([chr(154 - ord(c)) for c in "U(17zX(%,5.z^5(17l8(%,5.Z*(93-965$l7+-"])'

1 2 3 4