OVH Cloud OVH Cloud

Passage des parametres par référence/valeur

10 réponses
Avatar
Christophe
Bonjour,

Une petite question me turlupine depuis que je débute Python :
Est-il possible de passer des paramètres d'une fonction par référence ?
Ou bien est-ce toujours par valeur ?
N'est-il pas possible comme en C d'utiliser une sorte de pointeur pour
simuler un passage par référence ?

Supposons que je veuille construire une fonction foo qui prennent en
entrée deux paramètres a et b et les modifie, est-ce qu'il y a un moyen
de faire autrement que :
a,b = foo (a,b)
(même pas sûr que cela marche soi dit en passant...)

Merci d'avance pour tout éclaircissement...

10 réponses

Avatar
Adrien Di Mascio
Bonjour,

Le Fri, 18 Feb 2005 02:14:39 +0100, Christophe a écrit :

N'est-il pas possible comme en C d'utiliser une sorte de pointeur pour
simuler un passage par référence ?


Les passages se font par référence. Simplement, certains types d'objets
sont non-modifiables (immutables), d'autres le sont (mutables).
Exemple :
def f(a, b):
... print id(a) # Affiche l'identité de l'objet



... print id(b)
... a += b
... print id(a) # a peut parfois désigner un objet suivant les cas
...
a = 12 # Les entiters sont des objets non-modifiables !
b = 13
id(a)
135523628



id(b)
135523616



f(a, b) # Les identifiants imprimés sont bien les mêmes
135523628



135523616
135523472
a # L'objet que l'identifiant 'a' désigne n'a pas été modifié !
12



b
13



a = list('abc') # Les listes sont des objets modifiables !
b = list('def')
id(a)
-1210079796



id(b)
-1210079988



f(a, b) # Les identifiants sont toujours les mêmes
-1210079796



-1210079988
-1210079796
a # <-- L'objet que l'identifiant 'a' désigne a été modifié
['a', 'b', 'c', 'd', 'e', 'f']




Pour résumer, si tu passes un paramètre à une fonction et que tu tentes
de le modifier à l'intérieur de cette fonction, deux possibilités :

- l'objet est mutable => en sortant de la fonction, tous les
identifiants qui désignent cet objet, désignent toujours le même
et objet, mais il a été modifié, tu as donc accès à sa nouvelle
valeur.
- l'objet est immutable => en sortant de la fonction, tous les
identifiants qui désignent cet objet, désignent toujours le même et
objet, mais il n'a *pas* été modifié.

En espérant que ça aide,
Cordialement,
Adrien.

--
Adrien Di Mascio
LOGILAB, Paris (France).
http://www.logilab.com http://www.logilab.fr http://www.logilab.org



Avatar
jean-michel
"Christophe" a écrit dans le message de
news:
Bonjour,

Une petite question me turlupine depuis que je débute Python :
Est-il possible de passer des paramètres d'une fonction par référence ?
Ou bien est-ce toujours par valeur ?
N'est-il pas possible comme en C d'utiliser une sorte de pointeur pour
simuler un passage par référence ?

Supposons que je veuille construire une fonction foo qui prennent en
entrée deux paramètres a et b et les modifie, est-ce qu'il y a un moyen
de faire autrement que :
a,b = foo (a,b)
(même pas sûr que cela marche soi dit en passant...)

Merci d'avance pour tout éclaircissement...


La réponse à la question est oui et non; ça dépend de ce que sont a et b.
Non si ce sont des entiers ou des chaînes, oui si ce sont des listes.

Cela tient à la façon dont Python fait les assignations. Dixit le tutoriel,
les assignations ne copient pas les données, elles établissent des liens sur
des objets.
Si tu fait: a=b=1 , a et b pointent sur le même entier 1. Si tu fait b+=1 ,
b ne pointe plus sur le 1 pointé aussi par a, mais sur 2. Le fait
d'incrémenter b ne change pas à proprement parler sa valeur, mais le fait
pointer sur autre chose. En fait, dire 'a est un entier' est un abus de
langage. On devrait dire 'a est un pointeur sur un entier'.

Donc dans ton exemple, qui pourrait être:
#---------
def foo(a,b):
a+=1
b+=2
return a,b
a=b=1
b+=1
print a,b
a,b=foo(a,b)
print 'foo:',a,b

#---------
On obtient:
1 2
foo: 2 4
La ligne a,b=foo(a,b) réassigne a et b aux valeurs retournées par foo(a,b).
De son côté, la fonction foo a créé son propre espace de noms local, dans
lequel les a et b de foo pointaient vers les a et b de __main__. Toute modif
faite localement à cette fonction ne fait que réassigner les a et b locaux,
qui sont perdus au retour de la fonction. D'où la nécessité du return a,b.
C'est plus facile à comprendre si on définit foo avec d'autres noms (par
exemple 'def foo(i,j):'.
Je ne connais pas de moyen de passer une variable par référence, et
d'ailleurs ça ne me manque pas puisque a,b=foo(a,b) marche très bien. De
plus, si ça existe, c'est à mon avis du hors piste.

Maintenant, ça change un peu si le paramètre passé à une fonction est une
liste (ou un dico), c'est à dire pour être rigoureux, un pointeur vers une
liste.
Dans ce cas, la fonction crée toujours son propre espace local, a et b
pointent sur les mêmes listes que dans l'espace __main__, mais comme ce
qu'on va modifier c'est la valeur des éléments des listes, et non les listes
elles-même, les modifs faites dans foo3 sur les éléments des listes a et b
seront visibles dans __main__ au retour de la fonction. Subtil.
Cela donne par exemple:
#---------
def foo3(a,b):
a[0]+0
b[0]+ 0
a=b=[1]
a.append(1)
b.append(1)
foo3(a,b)
print 'foo3:',a[0],b[0]
a=b={}
a[0]=b[0]=1
foo3(a,b)
print 'foo3:',a[0],b[0]
#---------
et on obtient:
foo3: 301 301
foo3: 301 301

Ce qu'il est utile de se rappeler, c'est qu'une fonction se crée un espace
local, qui sera perdu au retour de la fonction, et que les assignations ne
font que créer et défaire des liens.
Bien entendu, tout ceci n'est que ma compréhension du système. Comme je suis
encore jeune en Python, j'attend impatiemment les autres réactions...

++jm

Avatar
Eric Brunel
On Fri, 18 Feb 2005 09:00:05 +0100, Adrien Di Mascio wrote:

Bonjour,

Le Fri, 18 Feb 2005 02:14:39 +0100, Christophe a écrit :

N'est-il pas possible comme en C d'utiliser une sorte de pointeur pour
simuler un passage par référence ?


Les passages se font par référence. Simplement, certains types d'objets
sont non-modifiables (immutables), d'autres le sont (mutables).


Mouais... Je ne sais pas si c'est la meilleure façon d'expliquer ce qui se passe. Le caractère mutable/immutable des objets n'a pas grand chose à voir avec la choucroute...

Exemple :
def f(a, b):
... print id(a) # Affiche l'identité de l'objet



... print id(b)
... a += b
... print id(a) # a peut parfois désigner un objet suivant les cas


Désolé, mais mauvais exemple. Remplace cette définition par:

def f(a, b):
a = b

et ton explication tombe à l'eau, parce que après l'appel de la fonction, l'objet pointé par a dans l'appelant n'est *jamais* modifié, que ce soit un entier (immutable) ou une liste (mutable).

Une explication approfondie est donnée ici en [1] (en anglais):

Voici une tentative de traduction des points importants:
- Parler de "variables" qui ont une "valeur" en Python est un peu confusant. En fait, il vaudrait mieux parler d'"objets" qui ont des "noms". Le concept de "nom" correspond plus ou moins au concept de "variable", mais pas tout à fait: une variable C - par exemple - est un espace mémoire dans lequel on peut mettre une valeur; en Python, ce sont les objets qui ont un espace mémoire associé et les "variables" sont juste des noms attachés aux objets.
- Une assignation est juste l'ajout d'un nom à un objet (ça incrémente aussi le compteur de références de l'objet, mais c'est une autre histoire)
- Les objets ne connaissent pas les noms sous lesquels ils sont référencés. Ils connaissent uniquement le nombre de noms; c'est le compteur de références, utilisé pour libérer l'objet une fois que plus personne ne le connait.
- Lorsqu'on appelle une fonction en lui passant un paramètre a, on lui passe l'*objet* référencé par la variable a. L'objet ne connaissant pas ses différents noms, il n'y a *aucun* moyen de modifier le "contenu de la variable a" dans l'appelant, c'est à dire l'objet pointé par le nom a. Par contre, il y a moyen de modifier l'*objet* pointé par a, si toutefois cet objet est "mutable" et s'il offre des méthodes permettant de le modifier. Dans ce cas, l'objet pointé par le nom a dans l'appelant *ne change pas*. C'est son contenu qui change.
- Dernier point: quand on fait:
x.a = y
ou
x[i] = y
ou
x += y
ce sont - en gros - juste des raccoucis pour x.__setattr__('a', y), x.__setitem__(i, y) et x.__iadd__(y), respectivement. Ce sont donc des appels de méthode sur l'objet. Ce qui explique que:

def f(a, b):
a += b
x = [0, 1]
y = [2, 3]
f(x, y)

*semble* modifier la variable x. Mais ce n'est en fait pas le cas: le nom x pointe toujours sur le meme objet liste; c'est cet objet qui a été modifié via sa méthode __iadd__ appelée par le +=.

Par contre:

def f(a, b):
a = b
x = [0, 1]
y = [2, 3]
f(x, y)

ne fait qu'ajouter le nom a à l'objet pointé par b (c'est à dire la liste [2, 3]) et n'appelle aucune méthode sur cet objet. Il n'est donc pas modifié, et rien ne sort de la fonction.


Et un jour, je prendrai mon courage à deux mains, je traduirai correctement la page [1] et je la mettrai en ligne quelque part. Vu le nombre de fois ou cette question est posée, je sens que ça pourrait aider...

En attendant, HTH
- Eric Brunel -

[1] http://www.effbot.org/zone/python-objects.htm




Avatar
Do Re Mi chel La Si Do
Bonjour !


Je partage tes propos, même si je ne pense pas que j'aurais pu trouver
d'aussi bonnes tournures de phrases.

Je voulais juste dire que ces paragraphes expliquent aussi la confusion que
beaucoup font à propos du typage et de Python. Beaucoup ont l'habitude
d'associer les types aux variables, alors que, avec Python, ce sont les
objets qui sont typés (et pas les noms).


Et enfin, je me demande si l'emploi systématique du terme "pointeur", à la
place de "variable" ne serait pas une solution.


@-salutations
--
Michel Claveau
Avatar
Eric Brunel
On Fri, 18 Feb 2005 14:16:41 +0100, Do Re Mi chel La Si Do wrote:
Bonjour !


Je partage tes propos, même si je ne pense pas que j'aurais pu trouver
d'aussi bonnes tournures de phrases.


Grands mercis, n'en jetez plus, c'est trop...

Je voulais juste dire que ces paragraphes expliquent aussi la confusion que
beaucoup font à propos du typage et de Python. Beaucoup ont l'habitude
d'associer les types aux variables, alors que, avec Python, ce sont les
objets qui sont typés (et pas les noms).


Et enfin, je me demande si l'emploi systématique du terme "pointeur", à la
place de "variable" ne serait pas une solution.


Compte tenu du fait que les pointeurs existent aussi en C/C++ et que la notion n'est pas vraiment la meme en Python, ça ne me parait pas une très bonne idée...

En fait, j'aime bien ces termes de "nom" et d'"objet" à la place de "variable" et "valeur": ça a l'avantage de ne pas trop déclencher d'associations hasardeuses dans la tete du lecteur. Vu que la façon de faire de Python est assez spécifique, c'est plutot un avantage.

Mais bon, juste mes €0,02...
- eric -

Avatar
Christophe
[snip l'explication très claire]

Ok je pense avoir compris, merci pour ton aide !
Avatar
Christophe
On Fri, 18 Feb 2005 15:30:37 +0100, "Eric Brunel"

Je partage tes propos, même si je ne pense pas que j'aurais pu
trouver d'aussi bonnes tournures de phrases.


Grands mercis, n'en jetez plus, c'est trop...


Si, si ! moi aussi j'ai compris grâce à tes explications ! ;-)


Je voulais juste dire que ces paragraphes expliquent aussi la
confusion que beaucoup font à propos du typage et de Python.
Beaucoup ont l'habitude d'associer les types aux variables, alors
que, avec Python, ce sont les objets qui sont typés (et pas les
noms).


Et enfin, je me demande si l'emploi systématique du terme
"pointeur", à la place de "variable" ne serait pas une solution.


Compte tenu du fait que les pointeurs existent aussi en C/C++ et que
la notion n'est pas vraiment la meme en Python, ça ne me parait pas
une très bonne idée...

En fait, j'aime bien ces termes de "nom" et d'"objet" à la place de
"variable" et "valeur": ça a l'avantage de ne pas trop déclencher
d'associations hasardeuses dans la tete du lecteur. Vu que la façon de
faire de Python est assez spécifique, c'est plutot un avantage.



En fait ayant fait beaucoup de C, c'est comme ça que je me suis à peu
près représenté la chose : il est impossible de changer la valeur d'une
variable passée en paramètre, par contre dans le cas d'un vecteur ou
d'un tableau, la variable n'est qu'un pointeur vers les données, données
qui elles sont modifiables (mais pas le pointeur lui même).

Je ne suis pas trop sûr que ce soit une bonne représentation, mais c'est
un début de compréhension (je débute en Python !).

Merci !


Avatar
Eric Brunel
On Tue, 22 Feb 2005 00:30:26 +0100, Christophe wrote:
[snip]
En fait ayant fait beaucoup de C, c'est comme ça que je me suis à peu
près représenté la chose : il est impossible de changer la valeur d'une
variable passée en paramètre, par contre dans le cas d'un vecteur ou
d'un tableau, la variable n'est qu'un pointeur vers les données, données
qui elles sont modifiables (mais pas le pointeur lui même).


Remplace "qui elles sont modifiables" par "qui elles peuvent etre modifiables" et c'est à peu près ça... Là, on en revient à ce que disait Adrien dans sa première réponse: certains objets sont mutables et d'autres pas; un objet non-mutable (par ex. un entier ou une chaine) passé à une fonction/méthode ne peut jamais voir son contenu modifié.
- eric -

Avatar
Laurent Pointal
Christophe wrote:

Bonjour,

Une petite question me turlupine depuis que je débute Python :
Est-il possible de passer des paramètres d'une fonction par référence ?
Ou bien est-ce toujours par valeur ?
N'est-il pas possible comme en C d'utiliser une sorte de pointeur pour
simuler un passage par référence ?

Supposons que je veuille construire une fonction foo qui prennent en
entrée deux paramètres a et b et les modifie, est-ce qu'il y a un moyen
de faire autrement que :
a,b = foo (a,b)
(même pas sûr que cela marche soi dit en passant...)

Merci d'avance pour tout éclaircissement...


J'ai écrit une petite page là dessus dans le wiki, il y a quelques années

http://wikipython.flibuste.net/moin.py/QuestionsGenerales

A priori c'est encore valable.

A+

Laurent.

Avatar
Jean-michel
"Laurent Pointal" a écrit dans le message de
news: 421cf659$0$19318$
Christophe wrote:

J'ai écrit une petite page là dessus dans le wiki, il y a quelques années

http://wikipython.flibuste.net/moin.py/QuestionsGenerales

A priori c'est encore valable.


Bien vu !
Ca m'apprendra à poster une réponse sans chercher d'abord ce qui existe !
++jm