2015-10-18 32 views
1

嘿,我正在从stackoverflow上的另一篇文章中研究代码,我注意到了一些关于“for循环”的内容。如果使用“pop”或“remove”来更改列表,则会混淆该内部循环的索引。如果您从列表中弹出/删除项目,它将跳过整个项目。我解决这个问题的方法是,在我操纵另一个列表时,实际制作一份列表的副本以用于“for循环”。我新来python。用于循环和操作的Python

我已添加到他的列表中。我的程序删除了超过180的任何人或任何名为joe的人。起初,我只是在注意到这个问题时才使用d_list。然后,我只是“temp_list = d_list”,我认为这是一个单独的副本,但我猜并没有。然后我使用列表的复制属性使其工作。这样我就不会操纵“for循环”使用的列表。

我的问题是,这是正常的,我有没有解决它的权利?对于我来说,如果数据很大,你不会想复制数据。我想出的另一种方法是使用while循环而不是外循环。

d_list = [ {'id':1, 'Name': 'Hannah', 'weight':150}, {'id':2, 'Name':'Andrew', 'weight':200}, {'id':3, 'Name':'Joe', 'weight':180}, 
      {'id':4, 'Name':'Joe', 'weight':180}, {'id':5, 'Name':'Steve', 'weight':200}, {'id':6, 'Name':'Joe', 'weight':180}, 
      {'id':7, 'Name':'George', 'weight':180}] 

temp_list = d_list 
#temp_list = d_list.copy() 
print(d_list) 
i = 0 
for item in temp_list: # may make a while loop 
    print(item, "i = ", i, end="[") 
    for k, v in item.items(): 
     print(end="*") 
     if (k == "weight") and (v > 180): 
      d_list.pop(i) 
      print('^popped^', i, end="") # <-- pop but you need an index 
      i -= 1 
     elif (k == "Name") and (v == "Joe"): 
      d_list.remove(item)   # <-- remove just uses item to find and remove 
      print("^removed^", i, end="") 
      i -= 1 
    i += 1 
    print("]") 
print(d_list) 
print("i = ", i) 
+1

这不是平常的,因为'弹出列表和删除列表通常不用于有效的解决方案。通常对于简单的事情,你会使用列表理解。对于更复杂的事情,像@alexis回答中的for循环。这对初学者来说是不直观的,但是制作一个全新的列表通常比以这种方式修改一个列表更有效。 –

回答

0

temp_list = d_list创建参考所以在任一列表中的任何更改都将反映在,这样就肯定是不行的。 temp_list = d_list.copy()创建一个浅表副本将工作和会temp_list = d_list[:]但更好的方法,以避免任何复制都将是使用reversed,只是从列表中删除的元素:

for item in reversed(d_list): 
    if item.get("weight", 0) > 180 or item.get("Name") == "Joe": 
     d_list.remove(item) 
     i -= 1 

如果你想弹出你可以开始在使用范围内以相反的端部:

for i in range(len(d_list) -1 , -1, - 1): 
    item = d_list[i] 
    if item.get("weight", 0) > 180 or item.get("Name") == "Joe": 
     i -= 1 

第三种选择是使用list comprehensiond_list[:]突变原始对象/列表:

d_list[:] = [d for d in d_list if d.get("weight", 0) <= 180 and d.get("Name") != "Joe"] 

或者用generator expression结合起来:

d_list[:] = (d for d in d_list if d.get("weight", 0) <= 180 and d.get("Name") != "Joe") 

所有这些方法都将给你相同的输出。使用dict.get而不是迭代所有项目也是更有效的解决方案,我们每次迭代执行两次查找,而不是查看每个字典中的所有键和值。

使用python3一些计时:

In [14]: %%timeit 
d_list = [{'id': 1, 'Name': 'Hannah', 'weight': 150}, {'id': 2, 'Name': 'Andrew', 'weight': 200}, 
      {'id': 3, 'Name': 'Joe', 'weight': 180}, 
      {'id': 4, 'Name': 'Joe', 'weight': 180}, {'id': 5, 'Name': 'Steve', 'weight': 200}, 
      {'id': 6, 'Name': 'Joe', 'weight': 180}, 
      {'id': 7, 'Name': 'George', 'weight': 180}] 
for item in reversed(d_list):  
    if item.get("weight", 0) > 180 or item.get("Name") == "Joe": 
     d_list.remove(item) 
    ....: 
100000 loops, best of 3: 4.35 µs per loop 

In [15]: %%timeit 
d_list = [{'id': 1, 'Name': 'Hannah', 'weight': 150}, {'id': 2, 'Name': 'Andrew', 'weight': 200}, 
      {'id': 3, 'Name': 'Joe', 'weight': 180}, 
      {'id': 4, 'Name': 'Joe', 'weight': 180}, {'id': 5, 'Name': 'Steve', 'weight': 200}, 
      {'id': 6, 'Name': 'Joe', 'weight': 180}, 
      {'id': 7, 'Name': 'George', 'weight': 180}] 
for i in range(len(d_list) - 1, -1, - 1): # may make a while loop 
    item = d_list[i] 
    if item.get("weight", 0) > 180 or item.get("Name") == "Joe": 
     d_list.pop(i) 
    ....: 
    ....:  
100000 loops, best of 3: 4.48 µs per loop 

In [16]: %%timeit 
    ....: d_list = [{'id': 1, 'Name': 'Hannah', 'weight': 150}, {'id': 2, 'Name': 'Andrew', 'weight': 200}, 
    ....:   {'id': 3, 'Name': 'Joe', 'weight': 180}, 
    ....:   {'id': 4, 'Name': 'Joe', 'weight': 180}, {'id': 5, 'Name': 'Steve', 'weight': 200}, 
    ....:   {'id': 6, 'Name': 'Joe', 'weight': 180}, 
    ....:   {'id': 7, 'Name': 'George', 'weight': 180}] 
    ....: d_list[:] = (d for d in d_list if d.get("weight", 0) <= 180 and d.get("Name") != "Joe") 
    ....: 
100000 loops, best of 3: 3.23 µs per loop 

In [17]: %%timeit 
d_list = [{'id': 1, 'Name': 'Hannah', 'weight': 150}, {'id': 2, 'Name': 'Andrew', 'weight': 200}, 
      {'id': 3, 'Name': 'Joe', 'weight': 180}, 
      {'id': 4, 'Name': 'Joe', 'weight': 180}, {'id': 5, 'Name': 'Steve', 'weight': 200}, 
      {'id': 6, 'Name': 'Joe', 'weight': 180}, 
      {'id': 7, 'Name': 'George', 'weight': 180}] 
d_list[:] = [d for d in d_list if d.get("weight", 0) <= 180 and d.get("Name") != "Joe"] 
    ....: 
100000 loops, best of 3: 2.98 µs per loop 

所以列表比较是最快其次是根EXP。如果你知道密钥总是存在,那么访问d["weight"] etc ..也会再次更快

+0

谢谢。有效。 – James

+0

如何使用while循环而不是为了循环而颠倒?无论哪种方式都有优势 – James

+0

@詹姆斯,使用反向使用没有额外的空间 –

1

由于你确定的问题,这种事情最好通过创建一个新的限定元素列表来完成。另外,扫描所有的键和值都很愚蠢,字典是为了通过查找键可用于:

newlist = [] 
for item in d_list: 
    if item["weight"] <= 180 and item["Name"] != "Joe": 
     newlist.append(item) 

然后,您可以腾出旧列表,如果你担心“浪费”空间:

del d_list