59 votes

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?

85voto

coldspeed Points 111053

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

L' words[:] , c'est la magie de la sauce ici.

Observer:

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

Et maintenant, sans l' [:]:

>>> 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 l' words[:] renvoie un copy de la liste existante, de sorte que vous êtes une itération sur une copie, qui n'est pas modifié.

Vous pouvez vérifier si vous êtes en se référant aux mêmes listes à l'aide de 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 intéressant de noter que, [i:j] est appelé le découpage de l'opérateur, et ce qu'il fait c'est qu'elle renvoie une nouvelle copie de la liste, en commençant à partir de l'index i, jusqu'à (mais pas y compris index) j.

Donc, words[0:2] vous donne

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

En omettant l'indice de départ signifie la valeur par défaut est 0, tout en omettant le dernier indice signifie que la valeur par défaut est len(words), et le résultat final est que vous recevez une copie de l' ensemble de la liste.


Si vous voulez faire de votre code un peu plus lisible, je vous recommande l' copy module.

from copy import copy 

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

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

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

11voto

inspectorG4dget Points 25092

words[:] des copies de tous les éléments en words dans une nouvelle liste. Ainsi, lorsque vous parcourez words[:], que vous êtes en train de parcourir tous les éléments qui words actuellement. Ainsi, lorsque vous modifiez words, les effets de ces modifications ne sont pas visibles en words[:] (parce que vous avez appelé sur words[:] avant de commencer à modifier words)

Dans le dernier exemple, vous êtes à parcourir words, ce qui signifie que toutes les modifications que vous apportez aux words est en effet visible de l'itérateur. Par conséquent, lorsque vous insérez dans l'index de 0 words, vous "bosse" de tout autre élément en words par un index. Donc, quand vous passez à la prochaine itération de ta boucle for, vous obtiendrez l'élément lors de la prochaine indice de words, mais c'est juste l'élément que vous venez de voir (parce que vous avez inséré un élément au début de la liste, le déplacement de l'autre élément par un indice).

Pour le voir 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)

5voto

Denis Kuzin Points 530

(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 rapportent 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.

1voto

Bibek Ghimire Points 300

Jetons un coup d'oeil à l'itérateur et iterables:

Un objet iterable est un objet qui a un __iter__ méthode qui retourne un itérateur, ou qui définit un __getitem__ méthode qui peut prendre séquentielle indices en partant de zéro (et soulève une IndexErrorlorsque les index ne sont plus valides). Si un objet iterable est un objet que vous pouvez obtenir un itérateur de.

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

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

Dans ton premier cas:

for w in words[:]

L' for boucle va se répéter sur la nouvelle copie de la liste pas les paroles originales. Tout changement dans les mots n'a pas d'effet sur l'itération de boucle, et la boucle se termine normalement.

C'est de cette façon que la boucle fait son travail:

  1. boucle d'appels iter méthode sur un objet iterable et parcourt l'itérateur

  2. boucle d'appels next méthode iterator objet pour obtenir l'élément suivant de itérateur. Cette étape est répétée jusqu'à ce qu'il n'y a pas plus d'éléments à gauche

  3. boucle s'arrête quand un StopIteration exception 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 êtes une itération sur l'original de la liste de mots et d'ajouter des éléments à mots ont un impact direct sur l'itérateur de l'objet. Donc, à chaque fois vos mots est mis à jour, l'itérateur de l'objet est également mis à jour et donc crée une boucle infinie.

Regardez ceci:

>>> 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 d' StopIteration , vous obtiendrez les mises à jour d'itérateur et next retourne en conséquence. C'est pourquoi votre boucle s'exécute à l'infini.

Pour en savoir plus sur l'itération et l'itération de protocole, vous pouvez regarder ici.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X