python获取数组下标 - python遍历key




这段代码中list (3)

这个问题在这里已有答案:

此代码来自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)

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


我们来看看迭代器和迭代:

iterable是一个具有 __iter__ 方法的对象,该方法返回一个迭代器,或者定义一个 __getitem__ 方法,该方法可以从零开始执行顺序索引(并在索引不再有效时引发 IndexError )。 因此,iterable是一个可以从中获取迭代器的对象。

迭代器是具有 next (Python 2)或 __next__ (Python 3)方法的对象。

iter(iterable) 返回迭代器对象, list_obj[:] 返回一个新的列表对象,list_object的精确副本。

在你的第一个案例中:

for w in words[:]

for 循环将迭代列表的新副本而不是原始单词。 单词的任何更改都不会对循环迭代产生影响,并且循环正常终止。

这就是循环的工作方式:

  1. 循环在iterable上调用 iter 方法并迭代迭代器

  2. 循环调用迭代器对象上的 next 方法从迭代器获取下一个项目。 重复此步骤,直到不再有元素为止

  3. 当引发 StopIteration 异常时,循环终止。

在你的第二种情况:

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 返回。 这就是你的循环无限运行的原因。

有关迭代和迭代协议的更多信息,请查看 here


这是陷阱之一! python,可以逃脱初学者。

words[:] 这个 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[:] 返回现有列表的 copy ,因此您将迭代一个未修改的副本。

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

在第一种情况下:

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

在第二种情况:

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

值得注意的是, [i:j] 被称为 切片运算符 ,它的作用是返回从索引 i 开始的列表的新副本,直到(但不包括)索引 j

所以, words[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() 来做同样的事情。


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

在后一个示例中,您将迭代 words ,这意味着您对 words 所做的任何更改确实对迭代器可见。 因此,当您插入 words 索引0时,您会“升高” words 的每个其他元素一个索引。 因此,当您继续执行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)




iteration