What is the meaning of list[:] in this code?

This code is from Python's Documentation. I'm a little confused.

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

And the following is what I thought at first:

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

Why does this code create a infinite loop and the first one doesn't?

This is one of the gotchas! of python, that can escape beginners.

The words[:] is the magic sauce here.

Observe:

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

And now without the [:]:

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

The main thing to note here is that words[:] returns a copy of the existing list, so you are iterating over a copy, which is not modified.

You can check whether you are referring to the same lists using id():

In the first case:

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

In the second case:

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

It is worth noting that [i:j] is called the slicing operator , and what it does is it returns a fresh copy of the list starting from index i, upto (but not including) index j.

So, words[0:2] gives you

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

Omitting the starting index means it defaults to 0, while omitting the last index means it defaults to len(words), and the end result is that you receive a copy of the entire list.

If you want to make your code a little more readable, I recommend the 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)

This basically does the same thing as your first code snippet, and is much more readable.

Alternatively (as mentioned by DSM in the comments) and on python >=3, you may also use words.copy() which does the same thing.

From: stackoverflow.com/q/44633798

Back to homepage or read more recommendations: