在python中迭代和更新列表[重复]
Posted
技术标签:
【中文标题】在python中迭代和更新列表[重复]【英文标题】:Iterating and Updating the list in python [duplicate] 【发布时间】:2017-12-17 22:55:04 【问题描述】:我无法理解为什么以下代码会无限期运行 循环(当我不使用副本列表时)
list = ["Mohit","kumar","sffsfshfsd"]
for w in list:
if(len(w)) > 5:
list.insert(0,w)
print("inside loop")
print(list)
上面的代码无限期地打印inside loop。
现在,如果代替列表,我使用下面的副本列表可以正常工作。
list = ["mohit","kumar","sffffgssddf"]
for w in list[:]:
if len(w) > 5:
list.insert(0,w)
print("inside loop")
print(list)
现在我在 python 文档中读到这是行为 我会得到,但我想了解其背后的原因。谢谢在 前进。
【问题讨论】:
旁注:永远不要在内置函数之后命名变量(例如list
)。
谢谢,会记住这一点@mpf82
一旦达到"sffsfshfsd"
每次迭代都会添加到列表的前面
您的列表在您对其进行迭代时不断增长,因此它永远不会停止。
但是我已经到了列表的第三个元素,为什么我们要在插入新元素之后循环回到第一个元素?
【参考方案1】:
第一个 for 循环 for w in list
将使用迭代器(来自 iter(list)
)来检索和循环列表中的每个项目。此迭代器不会立即获取整个列表 - 它是惰性,这意味着它在需要时一次只从列表中获取一个项目。您可以了解the iteration protocol here,或iteration/generators and laziness here。
循环遍历索引 0 和 1 什么都不做,因为它们的字符串长度小于 6。但是,在索引 2 处,您将 "sffsfshfsd"
添加到 list
的开头。现在list
已经增长,并且在索引 3 中有一些东西:"sffsfshfsd"
。然后迭代继续,从下一个索引 (3) 中选择值,该值再次在开头添加,将索引 3 处的相同值移动到索引 4...循环永远不会结束。
在您的第二个循环w in list[:]
中,您创建整个列表 (by using a slice operator) 的副本 并对其进行迭代。您正在将项目添加到原始列表,而不是副本,因此迭代器不会触及您添加的项目。
PS:我试图搜索 Python 源代码(它是 C)来证明列表迭代器实际上使用递增索引(如上所述)。我对阅读 Python 的源代码并不精通,但这是我在cpython/listobject.c 中找到的内容:
Iterator creation, sets starting index to 0
2797 static PyObject *
2798 list_iter(PyObject *seq)
2799
....
2806 it = PyObject_GC_New(listiterobject, &PyListIter_Type);
....
2809 it->it_index = 0;
....
2813 return (PyObject *)it;
2814
next
uses it->it_index
from above and then increments it
2831 static PyObject *
2832 listiter_next(listiterobject *it)
2833
....
2844 item = PyList_GET_ITEM(seq, it->it_index);
2845 ++it->it_index;
....
2847 return item;
....
2853
在我看来是合法的?
【讨论】:
【参考方案2】:为了模拟列表迭代在内部的工作方式,让我们使用整数索引和while
循环重写您的程序。
lst = ["Mohit", "kumar", "sffsfshfsd"]
pos = 0
while pos < len(lst):
word = lst[pos]
print('lst=%s pos=%d word=%s' % (lst, pos, word))
if len(word) > 5:
lst.insert(0, word)
pos += 1
下面显示了当你运行它时会发生什么:
lst=['Mohit', 'kumar', 'sffsfshfsd'] pos=0 word=Mohit
lst=['Mohit', 'kumar', 'sffsfshfsd'] pos=1 word=kumar
lst=['Mohit', 'kumar', 'sffsfshfsd'] pos=2 word=sffsfshfsd
lst=['sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=3 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=4 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=5 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=6 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=7 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=8 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=9 word=sffsfshfsd
...
(这种情况一直持续到您耗尽内存或耐心为止。)
如您所见,您不断将最后的 'sffsfshfsd'
移动到右侧,因此您的代码会一直查看它并且永不停止。
如果您在副本上工作,则不会发生这种情况,因为您不再修改您正在迭代的列表。
如果您在插入后调整循环索引也不会发生:
if len(word) > 5:
lst.insert(0, word)
pos += 1 # account for the extra word
pos += 1
或移动单词而不是复制它:
if len(word) > 5:
lst.insert(0, lst.pop(pos)) # don't change len(lst)
【讨论】:
【参考方案3】:发生这种情况是因为您在从第三次开始的每次迭代中都将“sffsfshfsd”附加到列表中,因此列表永远不会结束。
【讨论】:
【参考方案4】:在第一个代码中,您在循环的同一个列表中插入元素。这就是为什么它一直在内循环,因为列表无限增长。 在第二个代码中,您正在制作副本,将 for 循环和原始列表分开,因此它最终会停止。
【讨论】:
【参考方案5】:引用from the docs:
注意:当序列被 循环(这只能发生在可变序列,即列表)。一个 内部计数器用于跟踪接下来使用哪个项目,并且 这在每次迭代时递增。当这个计数器到达 循环终止的序列的长度。这意味着,如果 套件从序列中删除当前(或前一个)项目, 将跳过下一个项目(因为它获取当前项目的索引 已经处理过)。同样,如果套件插入 在当前项目之前的序列中的项目,当前项目将是 下次通过循环再次处理。这可能会导致令人讨厌的 可以通过使用切片创建临时副本来避免的错误 整个序列,例如,
for x in a[:]:
if x < 0: a.remove(x)
Python 中列表的 for 循环在内部维护一个计数器,用于获取下一项。
在您的第一个代码中,当它到达 sffsfshfsd
(即索引 2)时,您再次将其插入到列表的开头,因此所有项目移动一个位置,现在 sffsfshfsd
将移动到索引 3 并被拾取在下一次迭代中。然后继续……
在您的第二个代码中,您正在迭代列表的副本,并且在您修改原始列表时不会修改列表的副本。
lst = ["Mohit","kumar","sffsfshfsd"]
for i, w in enumerate(lst):
print("Index: i | List: list".format(i=i, list=lst))
if(len(w)) > 5:
lst.insert(0, w)
输出:
Index: 0 | List: ['Mohit', 'kumar', 'sffsfshfsd']
Index: 1 | List: ['Mohit', 'kumar', 'sffsfshfsd']
Index: 2 | List: ['Mohit', 'kumar', 'sffsfshfsd']
Index: 3 | List: ['sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd']
Index: 4 | List: ['sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd']
Index: 5 | List: ['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd']
Index: 6 | List: ['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd']
【讨论】:
【参考方案6】:我认为这是一个非常有趣的问题。我相信答案应该出现在 python 源代码实现中(抱歉我找不到它,希望专家可以指导我们使用 Python 实现)
for 循环不会创建原始数据的副本。因此,每次添加新数据时,循环都会继续。 (我不确定for循环是如何在实现层面实现的,我相信它可能会使用迭代器)
另一方面[:],这个操作符将创建一个原始数据集的新副本。因此,无论您如何更改原始数据集,for 循环都会在副本上循环(不会更改)。
证明如下:
list = ["mohit","kumar","sffffgssddf"]
test = list
list.append("test")
print test
#['mohit', 'kumar', 'sffffgssddf', 'test']
#clear data, let's try [:]
list = ["mohit","kumar","sffffgssddf"]
test = list[:]
list.append("test")
print test
#['mohit', 'kumar', 'sffffgssddf']
因此,在您的第二个示例中很清楚,您的 for 循环正在循环原始数据的副本。因此原始数据集更改不会影响复制数据。因此,您的第二个示例正在运行,第一个示例将无限循环。
希望对你有帮助。
【讨论】:
非常感谢@White。而其他人大多解决了我问题的第一部分。你也清除了第二部分。多谢 。 :) @Manya 不客气。等待专家给我们展示Python内核,这将是确定的答案。以上是关于在python中迭代和更新列表[重复]的主要内容,如果未能解决你的问题,请参考以下文章