Python 此代码中列表[:]的含义是什么?

Python 此代码中列表[:]的含义是什么?,python,list,for-loop,iteration,Python,List,For Loop,Iteration,这段代码来自Python的文档。我有点困惑 words = ['cat', 'window', 'defenestrate'] for w in words[:]: if len(w) > 6: words.insert(0, w) print(words) 以下是我最初的想法: words = ['cat', 'window', 'defenestrate'] for w in words: if len(w) > 6: words

这段代码来自Python的文档。我有点困惑

words = ['cat', 'window', 'defenestrate']
for w in words[:]:
    if len(w) > 6:
        words.insert(0, w)
print(words)
以下是我最初的想法:

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

为什么这段代码创建了一个无限循环而第一个没有呢?

这是一个陷阱!对于python来说,这可以逃避初学者

words[:]
是这里的神奇酱汁

注意:

>>> words =  ['cat', 'window', 'defenestrate']
>>> words2 = words[:]
>>> words2.insert(0, 'hello')
>>> words2
['hello', 'cat', 'window', 'defenestrate']
>>> words
['cat', 'window', 'defenestrate']
现在没有了
[:]

>>> words =  ['cat', 'window', 'defenestrate']
>>> words2 = words
>>> words2.insert(0, 'hello')
>>> words2
['hello', 'cat', 'window', 'defenestrate']
>>> words
['hello', 'cat', 'window', 'defenestrate']
这里需要注意的主要问题是
words[:]
返回现有列表的
副本
,因此您正在迭代一个未修改的副本

您可以使用
id()
检查是否引用了相同的列表:

在第一种情况下:

>>> words2 = words[:]
>>> id(words2)
4360026736
>>> id(words)
4360188992
>>> words2 is words
False
for w in words[:]
在第二种情况下:

>>> id(words2)
4360188992
>>> id(words)
4360188992
>>> words2 is words
True
words = ['cat', 'window', 'defenestrate']
for w in words:
    if len(w) > 6:
        words.insert(0, w)
print(words)
值得注意的是,
[i:j]
被称为切片操作符,它所做的是返回列表的一个新副本,从索引
i
,一直到(但不包括)索引
j

所以,
单词[0:2]
给你

>>> words[0:2]
['hello', 'cat']
省略起始索引意味着它默认为
0
,而省略最后一个索引意味着它默认为
len(words)
,最终结果是您收到整个列表的副本


如果您想让代码更具可读性,我建议使用
copy
模块

from copy import copy 

words = ['cat', 'window', 'defenestrate']
for w in copy(words):
    if len(w) > 6:
        words.insert(0, w)
print(words)
这与您的第一个代码片段基本相同,并且可读性更高

或者(正如DSM在评论中提到的)在python>=3上,您也可以使用
words.copy()
,它做同样的事情

(除了@Coldspeed answer)

请看以下示例:

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

它意味着名称
word
words2
指的是同一个对象

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


在本例中,我们创建了新对象。

words[:]
words
中的所有元素复制到一个新列表中。因此,当您迭代
words[:]
时,实际上是在迭代
words
当前拥有的所有元素。因此,当您修改
单词时,这些修改的效果在
单词[:]
中不可见(因为您在开始修改
单词之前调用了
单词[:]

在后一个示例中,您正在迭代
单词
,这意味着您对
单词
所做的任何更改都确实对迭代器可见。因此,当您在
单词
的索引0中插入时,您会将
单词
中的每个其他元素“增加”一个索引。因此,当您继续进行for循环的下一次迭代时,您将在
words
的下一个索引处获得元素,但这正是您刚才看到的元素(因为您在列表的开头插入了一个元素,将所有其他元素上移了一个索引)

要查看此操作,请尝试以下代码:

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)

让我们看看迭代器和iterables:

iterable是具有返回 迭代器,或它定义了一个可以 从零开始的顺序索引(当 索引不再有效)。所以iterable是一个对象 可以从中获取迭代器

迭代器是具有
next
(Python2)或
\uuuuuuuuuuu
(Python3)方法的对象

iter(iterable)
返回迭代器对象,
list\u obj[:]
返回一个新的list对象,即list\u对象的精确副本

在第一种情况下:

>>> words2 = words[:]
>>> id(words2)
4360026736
>>> id(words)
4360188992
>>> words2 is words
False
for w in words[:]
for
循环将迭代列表的新副本,而不是原始单词。单词的任何更改对循环迭代没有影响,循环正常终止

以下是循环的工作方式:

  • 循环调用iterable上的
    iter
    方法并在迭代器上迭代

  • 循环调用迭代器对象上的
    next
    方法,以从迭代器中获取下一项。重复此步骤,直到没有其他元素

  • 当引发
    StopIteration
    异常时,循环终止

  • 在第二种情况下:

    >>> id(words2)
    4360188992
    >>> id(words)
    4360188992
    >>> words2 is words
    True
    
    words = ['cat', 'window', 'defenestrate']
    for w in words:
        if len(w) > 6:
            words.insert(0, w)
    print(words)
    
    您正在对原始列表单词进行迭代,并向单词添加元素对迭代器对象有直接影响。因此,每次更新单词时,相应的迭代器对象也会更新,从而创建一个无限循环

    看看这个:

    >>> 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'
    
    每次在
    StopIteration
    之前更新原始列表时,您都会得到更新的迭代器,
    next
    会相应地返回。这就是循环无限运行的原因


    有关迭代和迭代协议的更多信息,您可以查看。

    coz,您在每次迭代时都要在
    单词
    列表中插入一个元素-)第一个元素是首字母
    单词
    的副本,而不是
    单词
    本身,您正在遍历在开始添加内容之前获取的
    单词的副本。在第二个示例中,您试图循环遍历
    单词
    ,同时使
    单词
    变长,因此永远不会到达结尾。示例遍历数组的副本,而您的示例遍历列表本身。复制时,不会插入新词,因此它只循环3个值。您可以在那里添加更好的答案@cᴏʟᴅsᴘᴇᴇᴅ. FWIW,这里的简短回答确实解释了这个问题背后的原因。@Coldspeed当然-你可以把它写成
    words[:]=[w为w,如果len(w)>6][::-1]+words
    …Jon,我提到过“更可读性”,而不是更少…:P@Coldspeed更具可读性:
    words[:0]=[w代表words中的w,如果len(w)>6]
    。我建议强调使用
    is
    而不是
    id()
    ,以查看您是否有不同的对象,因为当对象超出范围且