Influence d'un tableau lors d'un append

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour, Cela faisait 1h que je ne comprenais pas d'où venait un bug de mon programme et j'ai finalement compris mais pas trouvé de solution. Voici un exemple du bug

1
2
3
4
5
6
7
8
testGlobal = [[]]
tests = []
tests.append([a,b])
tests.append([b,c])
testGlobal.append(tests)
print(testGlobal)
tests.pop()
print(testGlobal)

Eh bien j'obtiens un résultat différent pour les 2 affichages. Alors que je n'ai pas modifié testGlobal. Cela signifie donc qu'avec append il y a toujours une dépendance entre l'objet inséré et la liste principale… (pourquoi ?) Comment éviter cela ? Merci d'avance

+0 -0

Bonsoir,

Ton problème vient du fait qu'en Python, les arguments des fonctions sont passés par référence. Plus généralement, tout est référence en Python. En d'autres termes, lorsque tu écris testGlobal.append(tests), ce n'est pas la valeur de ta variable tests qui est ajoutée à ta liste testGlobal, mais sa référence (une sorte d'adresse dans la mémoire).

Cet article t'expliquera bien mieux que moi cette notion : valeurs et références en Python.

Je te propose donc comme solutions, pour que ton code fonctionne :

1
testGlobal.append(tests.copy())

Ou :

1
testGlobal.append(tests[:])

Ou encore :

1
testGlobal.append(list(tests))

Je précise que mon niveau en Python n'est pas suffisamment bon pour que je puisse t'affirmer que tout ce que j'ai dit est correct.

Édité par Vulser

+0 -0

les arguments des fonctions sont passés par référence.

Vulser

C'est pas tout à fait un passage par référence comme on l'entendrait dans un autre langage (je pense par exemple à C++) : la réaffectation d'une variable reçue en paramètre n'a par exemple aucun impact sur la valeur initiale (je parle d'un opérateur d'affectation simple).

Il na s'agit donc pas vraiment d'un passage par référence, encore moins d'un passage par copie, on parle ainsi plutôt de passage par valeur.

Édité par entwanne

C'est pas tout à fait un passage par référence comme on l'entendrait dans un autre langage (je pense par exemple à C++) : la réaffectation d'une variable reçue en paramètre n'a par exemple aucun impact sur la valeur initiale (je parle d'un opérateur d'affectation simple).

Il na s'agit donc pas vraiment d'un passage par référence, encore moins d'un passage par copie, on parle ainsi plutôt de passage par valeur.

entwanne

Bien vu. En revanche, j'ai lu ici et que parler de passage par valeur serait une erreur. Que faut-il en penser ? Même si cette question n'est pas d'une grande importance, cela m'intrigue.

Par ailleurs, la FAQ Python parle de passage par affectation…

+0 -0

Bien vu. En revanche, j'ai lu ici et que parler de passage par valeur serait une erreur. Que faut-il en penser ? Même si cette question n'est pas d'une grande importance, cela m'intrigue.

Vulser

Oui, je voulais parler de passage par objet mais j'ai confondu les termes vu qu'il n'y a pas de différence entre objet et valeur en Python.
(j'aime d'ailleurs assez peu le terme de « passage par valeur » dans des langages tels que C ou C++, je préfère « passage par copie » où l'on comprend bien que la valeur est copiée)

Édité par entwanne

Staff

Pour en revenir à la question originelle : tout ce que l'on manipule en Python, qu'il s'agisse d'affectations de variable ou de passages d'arguments, sont des références. À moins de faire des copies explicites, une "liste d'objets" en python est une liste de références sur des objets.

En fait il suffit de comprendre comment sont représentés les scopes lexicaux dans CPython, pour ne plus jamais se tromper :

  • un scope (ou une closure) n'est rien d'autre qu'une table associative dont les clés sont des noms de variables et les valeurs des PyObject* : en C on parlerait de pointeur sur un objet, en Python on dira une référence.
  • En fait, vu que le langage dispose d'un garbage collector, l'image qui me semble la plus adaptée, c'est de voir les variables comme des std::shared_ptr<PyObject> en C++. C'est d'autant plus vrai que l'on peut créer des références "faibles" (weakref) en Python, de la même manière que l'on crée des std::weak_ptr en C++, pour casser les références cycliques et résoudre certains cas non triviaux qui bloquent le garbage collector.

Partant de là, ça ne me choque pas trop de parler de "passage par référence", mais j'avoue que ce détail sémantique me passe un peu au-dessus de la tête.

Édité par nohar

I was a llama before it was cool

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte