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

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


Avec a = [0] ça marche aussi. C'est comme ça que je fais, je n'ai pas
encore trouvé de solution plus propre malheureusement... Avis aux
experts ;-)



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).


non, promis, je ne ris pas... Enfin si, juste un peu.


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



De rien. Content que ça t'aide à avancer

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


En version 4, il utilise le clonage par défaut, en version 5 il utilise
les références.

Avatar
Guillaume Bouchard
Eric Brunel wrote:
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


Trés interessant en effet. J'ai lu dans google group que tu serais
interessés par la traduction, cadeau (modulo quelques fautes
d'orthographes et néologismes)

http://cipcgw.insa-lyon.fr/~gbouchard/python-objects-fr.htm

Si ma traduction est correcte, sachez que je la laisse jusqu'a ce que
mon compte INSA disparaisse (en esperarant que cela ne soit pas en Juin
;o) Si cela peut faire voir la lumière à d'autres :)

J'espère que ça t'aidera à comprendre la "philosophie" qui se cache
derrière tout ça.


Je pense que j'ai comprit un serieux truc là, merci BEAUCOUP.

--
Guillaume.

Avatar
bruno modulix
Guillaume Bouchard wrote:
(snip)

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


C'est pourtant simple : en Python, *tout* est objet, et on ne manipule
un objet *que* par une référence sur cet objet. Les 'variables' ne sont
que des noms associés à des références. Il ne peut donc pas y avoir de
passage par valeur. CQFD.

<digression>
Personnellement, je trouve ça beaucoup plus simple et beaucoup plus
logique que PHP4 où tout est passé par valeur sauf si on précise qu'on
passe une référence lors de l'appel - mais il faut penser à chaque fois
à se souvenir que telle fonction nécessite une référence - ou si la
fonction précise qu'elle attend une référence - mais dans ce cas on peut
oublier au moment de l'appeler qu'elle risque de modifier l'argument -
ou que PHP5 et Java, avec lequel on a des références systématiques pour
les objet et des valeurs (sauf si on précise, etc...) pour les types
"primitifs" (aheum), bref un système à deux vitesses.

Mais bon, j'ai bien vu récemment sur un blog quelqu'un affirmer que
Python était trop statique sur le typage et trop rigide en général
comparé à PHP... (no comment).
</digression>

Pour en revenir à nos moutons, dans la pratique, le passage par
référence ne m'a jamais posé le moindre problème en Python. Je n'utilise
pas d'objet mutable comme valeur par défaut d'un argument de fonction -
sauf à vouloir une variable 'statique', mais j'en ai rarement l'usage -
j'évite les listes imbriquées (quand je vois des listes imbriquées,
c'est pour moi un signe très clair qu'il est temps de définir une classe
spécifique), et d'une manière générale j'essaye d'éviter les effets de
bord dans les fonctions (encore un signe qu'il est temps de définir une
classe appropriée qui encapsulera les données sur lesquels se produisent
les effets de bord...)

N'essaie pas de faire du PHP en Python, cette approche n'a jamais été
payante quelque soit le langage. Le modèle de Python ne te sembles pas
logique parce qu'il est différent de ce que tu connais, mais (sans être
parfait, loin s'en faut) il possède sa propre cohérence.

--
bruno desthuilliers - moi aussi je peux faire du Perl:
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"

Avatar
bruno modulix
Yermat wrote:
(snip)

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.


Non, c'est *aussi* une référence. Simplement, 'modifier' cet objet dans
le code appelé revient en fait à faire pointer le nom sur un autre objet
- et comme le nom, lui, est local à la fonction appelée, cette nouvelle
affectation n'a pas d'incidence sur l'objet initialement pointé par le nom.

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"

Avatar
Guillaume Bouchard
bruno modulix wrote:
C'est pourtant simple : en Python, *tout* est objet, et on ne manipule
un objet *que* par une référence sur cet objet. Les 'variables' ne sont
que des noms associés à des références. Il ne peut donc pas y avoir de
passage par valeur. CQFD.


J'ai vraiment comprit le truc avec le text qu'a fournit Eric Brunel,
mais maitenant c'est comprit :)

N'essaie pas de faire du PHP en Python, cette approche n'a jamais été
payante quelque soit le langage.


Si je me met au Python c'est bien pour ne pas faire de php en Python. Je
ne suis pas fou au point de m'embeter a apprendre une nouvelle syntaxe
pour ne pas changer de méthode de developpement.

--
Guillaume.

Avatar
Yermat
Yermat wrote:
(snip)


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.



Non, c'est *aussi* une référence. Simplement, 'modifier' cet objet dans
le code appelé revient en fait à faire pointer le nom sur un autre objet
- et comme le nom, lui, est local à la fonction appelée, cette nouvelle
affectation n'a pas d'incidence sur l'objet initialement pointé par le nom.



Certes. Juste une question de où arrêter l'abstraction ;-)

--
Yermat


Avatar
bruno modulix
Jerome wrote:


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.


Les programmeurs PHP, je ne sais pas, mais les responsables de PHP
semblent déterminés à faire de PHP un mauvais sous-java.

(snip)

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


Boaf... Les vrais programmeurs codent en langage machine, c'est bien
connu !-)

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"


Avatar
bruno modulix
Jerome wrote:
(snip)

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,


Ah bon ? A part les instructions et les expressions, qu'est-ce qui n'est
pas objet en Python ?

probablement pour des raisons de performances. En
Smalltalk qui est réellement pur objet tu n'as que des références.


Je suis d'accord que Smalltalk soit encore plus extrêmistement OO que
Python, mais pour ce qui est des références, tu fais erreur : en Python,
il n'y a 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...


C'est pourtant ce que tu fais chaque fois que tu passes un objet
immutable à une fonction.

(snip)

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"

Avatar
Yermat
Jerome wrote:
(snip)


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,



Ah bon ? A part les instructions et les expressions, qu'est-ce qui n'est
pas objet en Python ?


Les entiers et les flotants...
dir(3)
['__abs__', '__add__', ...]



3.__class__
File "<stdin>", line 1



3.__class__
^
SyntaxError: invalid syntax
3.__add_(4)
File "<stdin>", line 1



3.__add_(4)
^
SyntaxError: invalid syntax

donc tout n'est pas objet.

Je suis d'accord que Smalltalk soit encore plus extrêmistement OO que
Python, mais pour ce qui est des références, tu fais erreur : en Python,
il n'y a que des références.


La preuve que non plus haut...

[...]



--
Yermat



1 2 3 4