web-dev-qa-db-fra.com

Python Liste & pour chaque accès (Rechercher / Remplacer dans la liste intégrée)

À 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
25
Syndacate

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 .

47
GreenMatt

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.

16
Ethan Furman

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']
6
Glider