在python中迭代和更新列表

时间:2017-07-13 08:54:19

标签: python list python-3.x for-loop python-2.x

  

我无法理解为什么以下代码无限期地进入   循环(当我不使用副本列表时)

list = ["Mohit","kumar","sffsfshfsd"]
for w in list:
    if(len(w)) > 5:
        list.insert(0,w)
    print("inside loop")

print(list)  

以上代码无限期打印内循环

现在,如果代替列表,我使用下面的副本列表工作正常。

list = ["mohit","kumar","sffffgssddf"]

for w in list[:]:
    if len(w) > 5:
        list.insert(0,w)
    print("inside loop")

print(list)  
  

现在我已经在python文档中读到这是行为   我会得到,但我想了解它背后的原因。谢谢   提前。

6 个答案:

答案 0 :(得分:5)

第一个for循环for w in list将使用迭代器(来自iter(list))来检索并循环遍历列表中的每个项目。这个迭代器不会立即获取整个列表 - 它是 lazy ,这意味着它只需要一次从列表中获取一个项目。您可以了解the iteration protocol hereiteration/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 }

似乎对我来说合法吗?

答案 1 :(得分:4)

要模拟列表迭代在内部的工作方式,请使用整数索引和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
...

(直到你用完RAM或耐心为止。)

正如您所看到的,您将最后的'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)

答案 2 :(得分:2)

之所以发生这种情况,是因为你在第三次开始的每次迭代中附加“sffsfshfsd”列表,所以列表永远不会结束。

答案 3 :(得分:2)

在第一个代码中,您将在循环的同一列表中插入元素。这就是为什么它继续在内循环,因为列表无限增长。 在第二个代码中,您正在复制,将for循环和原始列表分开,因此它最终会停止。

答案 4 :(得分:2)

引用http://yijunwang.azurewebsites.net/#/

  

注意:当序列被修改时,有一个微妙的   循环(这只能发生在可变序列,即列表中)。一个   内部计数器用于跟踪下一个使用的项目,以及   这在每次迭代时递增。当这个计数器到达时   循环终止的序列长度。这意味着,如果   suite从序列中删除当前(或前一个)项目   将跳过下一个项目(因为它获取当前项目的索引   已经治疗了)。同样,如果套件插入一个   当前项目之前的序列中的项目,当前项目将是   下次循环再次治疗。这可能导致讨厌   使用切片制作临时副本可以避免的错误   整个序列,例如,

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']

答案 5 :(得分:1)

我认为这是一个非常有趣的问题。我相信答案应该出现在python源代码实现中(抱歉,我找不到它,希望有专家可以指导我们进行Python实现)

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循环正在循环原始数据的副本。因此,原始数据集更改不会影响复制数据。因此,您的第二个示例正在运行,第一个示例将无限循环。

希望它有所帮助。