web-dev-qa-db-fra.com

Quelle est la signification de list [:] dans ce code?

Ce code provient de la documentation de Python. Je suis un peu confus.

words = ['cat', 'window', 'defenestrate']
for w in words[:]:
    if len(w) > 6:
        words.insert(0, w)
print(words)

Et voici ce que je pensais au début:

words = ['cat', 'window', 'defenestrate']
for w in words:
    if len(w) > 6:
        words.insert(0, w)
print(words)

Pourquoi ce code crée-t-il une boucle infinie et non la première?

58
S. Li

C'est l'un des pièges! de python, qui peut échapper aux débutants.

Le words[:] Est la sauce magique ici.

Observer:

>>> words =  ['cat', 'window', 'defenestrate']
>>> words2 = words[:]
>>> words2.insert(0, 'hello')
>>> words2
['hello', 'cat', 'window', 'defenestrate']
>>> words
['cat', 'window', 'defenestrate']

Et maintenant sans le [:]:

>>> words =  ['cat', 'window', 'defenestrate']
>>> words2 = words
>>> words2.insert(0, 'hello')
>>> words2
['hello', 'cat', 'window', 'defenestrate']
>>> words
['hello', 'cat', 'window', 'defenestrate']

La principale chose à noter ici est que words[:] Renvoie un copy de la liste existante, de sorte que vous effectuez une itération sur une copie, qui n'est pas modifiée.

Vous pouvez vérifier si vous vous référez aux mêmes listes en utilisant id():

Dans le premier cas:

>>> words2 = words[:]
>>> id(words2)
4360026736
>>> id(words)
4360188992
>>> words2 is words
False

Dans le second cas:

>>> id(words2)
4360188992
>>> id(words)
4360188992
>>> words2 is words
True

Il est à noter que [i:j] Est appelé l'opérateur de tranchage et renvoie une nouvelle copie de la liste à partir de index i, jusqu'à (sans inclure) index j.

Donc, words[0:2] Vous donne

>>> words[0:2]
['hello', 'cat']

Omettre l’index de départ signifie que la valeur par défaut est 0, Alors que le dernier index est omis, la valeur par défaut est len(words), et le résultat final est que vous recevez une copie du toute la liste .


Si vous voulez rendre votre code un peu plus lisible, je vous recommande le module copy.

from copy import copy 

words = ['cat', 'window', 'defenestrate']
for w in copy(words):
    if len(w) > 6:
        words.insert(0, w)
print(words)

Cela fait essentiellement la même chose que votre premier extrait de code et est beaucoup plus lisible.

Alternativement (comme mentionné par DSM dans les commentaires) et sur python> = 3, vous pouvez également utiliser words.copy() qui fait la même chose.

83
cs95

words[:] copie tous les éléments de words dans une nouvelle liste. Alors, quand vous parcourez words[:], vous parcourez tous les éléments que words a actuellement. Ainsi, lorsque vous modifiez words, les effets de ces modifications ne sont pas visibles dans words[:] _ (parce que vous avez appelé words[:] avant de commencer à modifier words)

Dans ce dernier exemple, vous parcourez words, ce qui signifie que tous les changements que vous apportez à words sont bien visibles pour votre itérateur. En conséquence, lorsque vous insérez dans l'index 0 de words, vous "bumpez" tous les autres éléments de words d'un index. Ainsi, lorsque vous passez à la prochaine itération de votre boucle for, vous obtenez l'élément au prochain index de words, mais c'est simplement l'élément que vous venez de voir (parce que vous avez inséré un élément au début). début de la liste, en déplaçant tous les autres éléments d’un index).

Pour voir cela en action, essayez le code suivant:

words = ['cat', 'window', 'defenestrate']
for w in words:
    print("The list is:", words)
    print("I am looking at this Word:", w)
    if len(w) > 6:
        print("inserting", w)
        words.insert(0, w)
        print("the list now looks like this:", words)
print(words)
11
inspectorG4dget

(En plus de @Coldspeed answer)

Regardez les exemples ci-dessous:

words = ['cat', 'window', 'defenestrate']
words2 = words
words2 is words

résultats: True

Cela signifie que les noms Word et words2 fait référence au même objet.

words = ['cat', 'window', 'defenestrate']
words2 = words[:]
words2 is words

résultats: False

Dans ce cas, nous avons créé le nouvel objet.

5
Denis Kuzin

Regardons les itérateurs et les itérables:

Un iterable est un objet ayant une méthode __iter__ Qui retourne un itérateur ou qui définit une méthode __getitem__ Pouvant prendre des index séquentiels à partir de zéro (et lève un IndexError lorsque les index ne sont plus valables). Donc, un itérable est un objet à partir duquel vous pouvez obtenir un itérateur.

Un itérateur est un objet avec une méthode next (Python 2) ou __next__ (Python 3).

iter(iterable) retourne un objet itérateur et list_obj[:] renvoie un nouvel objet liste, une copie exacte de list_object.

Dans votre premier cas:

for w in words[:]

La boucle for itérera sur la nouvelle copie de la liste et non sur les mots d'origine. Tout changement dans les mots n'a aucun effet sur l'itération de la boucle et celle-ci se termine normalement.

Voici comment la boucle fonctionne:

  1. boucle appelle iter méthode sur iterable et itère sur l'itérateur

  2. la boucle appelle la méthode next sur l'objet itérateur pour obtenir le prochain élément de l'itérateur. Cette étape est répétée jusqu'à ce qu'il ne reste plus d'éléments

  3. la boucle se termine quand une exception StopIteration est levée.

Dans votre deuxième cas:

words = ['cat', 'window', 'defenestrate']
for w in words:
    if len(w) > 6:
        words.insert(0, w)
print(words)

Vous parcourez les mots de la liste d'origine et ajoutez des éléments aux mots qui ont un impact direct sur l'objet itérateur. Ainsi, chaque fois que vos mots sont mis à jour, l'objet itérateur correspondant est également mis à jour et crée donc une boucle infinie.

Regarde ça:

>>> l = [2, 4, 6, 8]
>>> i = iter(l) # returns list_iterator object which has next method
>>> next(i)
2
>>> next(i)
4
>>> l.insert(2, 'A')
>>> next(i)
'A'

Chaque fois que vous mettez à jour votre liste d'origine avant StopIteration, vous obtenez l'itérateur mis à jour et next revient en conséquence. C'est pourquoi votre boucle fonctionne à l'infini.

Pour plus d'informations sur l'itération et le protocole d'itération, vous pouvez consulter ici .

1
Bibek Ghimire