À l'origine, je pensais que Python était un langage de passage par référence pur.
Venant de C/C++, je ne peux pas m'empêcher de penser à la gestion de la mémoire, et il est difficile de la sortir de ma tête. J'essaie donc de penser à cela d'un point de vue Java et de penser à tout sauf aux primitives comme un passage par référence.
Problème: J'ai une liste, contenant un tas d'instances d'une classe définie par l'utilisateur.
Si j'utilise la syntaxe for-each, c'est-à-dire:
for member in my_list:
print(member.str);
member
est-il l'équivalent d'une référence réelle à l'objet?
Est-ce l'équivalent de faire:
i = 0
while i < len(my_list):
print(my_list[i])
i += 1
Je pense que ce n'est pas le cas, car lorsque je cherche à faire un remplacement, cela ne fonctionne pas, c'est-à-dire que cela ne fonctionne pas:
for member in my_list:
if member == some_other_obj:
member = some_other_obj
Une simple recherche et remplacement dans une liste. Cela peut-il être fait dans une boucle pour chaque, si oui, comment? Sinon, dois-je simplement utiliser la syntaxe d'accès aléatoire (crochets), ou est-ce que cela ne fonctionnera PAS et je dois supprimer l'entrée et en insérer une nouvelle? C'est à dire.:
i = 0
for member in my_list:
if member == some_other_obj:
my_list.remove(i)
my_list.insert(i, member)
i += 1
Répondre à cela a été bon, car les commentaires ont conduit à une amélioration de ma propre compréhension des variables Python.
Comme indiqué dans les commentaires, lorsque vous parcourez une liste avec quelque chose comme for member in my_list
la variable member
est liée à chaque élément de liste successif. Cependant, la réaffectation de cette variable dans la boucle n'affecte pas directement la liste elle-même. Par exemple, ce code ne changera pas la liste:
my_list = [1,2,3]
for member in my_list:
member = 42
print my_list
Sortie:
[1, 2, 3]
Si vous souhaitez modifier une liste contenant des types immuables, vous devez faire quelque chose comme:
my_list = [1,2,3]
for ndx, member in enumerate(my_list):
my_list[ndx] += 42
print my_list
Sortie:
[43, 44, 45]
Si votre liste contient des objets mutables, vous pouvez modifier directement l'objet member
en cours:
class C:
def __init__(self, n):
self.num = n
def __repr__(self):
return str(self.num)
my_list = [C(i) for i in xrange(3)]
for member in my_list:
member.num += 42
print my_list
[42, 43, 44]
Notez que vous ne modifiez toujours pas la liste, il vous suffit de modifier les objets de la liste.
Vous pourriez bénéficier de la lecture Naming and Binding .
Python n'est pas Java, ni C/C++ - vous devez arrêter de penser de cette façon pour vraiment utiliser la puissance de Python.
Python n'a ni passe-par-valeur, ni passe-par-référence, mais utilise à la place pass-by-name (ou pass-by-object) - en d'autres termes, presque tout est lié à un nom que vous pouvez ensuite utilisation (les deux exceptions évidentes étant l'indexation de tuple et de liste).
Lorsque vous faites spam = "green"
, Vous avez lié le nom spam
à l'objet chaîne "green"
; si vous faites alors eggs = spam
vous n'avez rien copié, vous n'avez pas fait de pointeurs de référence; vous avez simplement lié un autre nom, eggs
, au même objet ("green"
dans ce cas). Si vous liez ensuite spam
à autre chose (spam = 3.14159
) eggs
sera toujours lié à "green"
.
Lorsqu'une boucle for s'exécute, elle prend le nom que vous lui donnez et la lie à son tour à chaque objet de l'itérable tout en exécutant la boucle; lorsque vous appelez une fonction, elle prend les noms dans l'en-tête de la fonction et les lie aux arguments passés; réattribuer un nom revient en fait à un nom (cela peut prendre un certain temps pour absorber cela - il l'a fait pour moi, de toute façon).
Avec les boucles for utilisant des listes, il existe deux méthodes de base pour affecter à nouveau à la liste:
for i, item in enumerate(some_list):
some_list[i] = process(item)
ou
new_list = []
for item in some_list:
new_list.append(process(item))
some_list[:] = new_list
Remarquez le [:]
Sur ce dernier some_list
- cela provoque une mutation des éléments de some_list
(Définissant le tout sur les éléments de new_list
) au lieu de relier le nom some_list
à new_list
. Est-ce important? Ça dépend! Si vous avez d'autres noms en plus de some_list
Liés au même objet de liste et que vous voulez qu'ils voient les mises à jour, alors vous devez utiliser la méthode de découpage; si vous ne le faites pas, ou si vous le faites pas voulez qu'ils voient les mises à jour, alors reliez - some_list = new_list
.
Vous pouvez remplacer quelque chose là-dedans en obtenant l'index avec l'élément.
>>> foo = ['a', 'b', 'c', 'A', 'B', 'C']
>>> for index, item in enumerate(foo):
... print(index, item)
...
(0, 'a')
(1, 'b')
(2, 'c')
(3, 'A')
(4, 'B')
(5, 'C')
>>> for index, item in enumerate(foo):
... if item in ('a', 'A'):
... foo[index] = 'replaced!'
...
>>> foo
['replaced!', 'b', 'c', 'replaced!', 'B', 'C']
Notez que si vous souhaitez supprimer quelque chose de la liste, vous devez itérer sur une copie de la liste, sinon vous obtiendrez des erreurs car vous essayez de modifier la taille de quelque chose que vous itérez. Cela peut être fait assez facilement avec des tranches.
Faux:
>>> foo = ['a', 'b', 'c', 1, 2, 3]
>>> for item in foo:
... if isinstance(item, int):
... foo.remove(item)
...
>>> foo
['a', 'b', 'c', 2]
Le 2 est toujours là parce que nous avons modifié la taille de la liste au fur et à mesure que nous l'avons itérée. La bonne façon serait:
>>> foo = ['a', 'b', 'c', 1, 2, 3]
>>> for item in foo[:]:
... if isinstance(item, int):
... foo.remove(item)
...
>>> foo
['a', 'b', 'c']