最近我了解了list和for循环,以及指示和删除列表中最后一项的命令.pop()
。
所以我尝试编写一个代码来逐个删除列表中的最后一项,直到它只剩下一个项目。
代码是:
list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
for i in list_A:
print(list_A.pop())
if 'c' not in list_A:
break
print("job done.")
python 3.6的输出给了我:
/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6
j
i
h
g
f
job done.
正如你所看到的,它确实有效,但只有一半呢?
我在期待:
j
i
h
g
f
e
d
c
job done
我的意思是,如果它返回一些错误,我会更舒服,这意味着代码不对。但为什么它有效,但不是完整的方式?
答案 0 :(得分:5)
你在迭代它时改变列表。
您可以使用while
循环执行此操作:
list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
while 'c' in list_A:
print(list_A.pop())
print('job done')
输出:
j i h g f e d c job done
更有效的方法是确定sentinel字符的第一个实例的索引,并删除它和列表的其余部分(尽管字符在删除时不会打印):
try:
pos = list_A.index('c')
list_A[:] = list_A[:pos]
# del list_A[pos:] # more efficient alternative suggested by @ShadowRanger
except ValueError as e:
pass
答案 1 :(得分:3)
在Python中使用for..in
循环时,您不应该修改列表。
这里发生的事情如下:
a
pop()
删除最后一个列表条目,因此在第一个循环迭代中,您将删除最后一个字母j
并将其打印e
会面时,您会从列表中删除并打印f
a
到e
的字母,因为您只是迭代e
循环的工作已完成很难说你想在这里做什么,因为它更多的是玩弄而不是完成任务。无论何时打算在循环内编辑列表,我建议使用while
循环。具有适当语义的示例可能如下所示:
while list_A:
print(list_A.pop())
if "c" not in list_A:
break
只要列表中有项目,此循环就会一直存在,只有在列表中没有c
后才会停止。
答案 2 :(得分:2)
我最近回答了一个类似的问题,归结为:不要修改你重复的序列。
使用自定义迭代器(来自another answer of mine)显示发生了什么:
class CustomIterator(object):
def __init__(self, seq):
self.seq = seq
self.idx = 0
def __iter__(self):
return self
def __next__(self):
print('give next element:', self.idx)
for idx, item in enumerate(self.seq):
if idx == self.idx:
print(idx, '--->', item)
else:
print(idx, ' ', item)
try:
nxtitem = self.seq[self.idx]
except IndexError:
raise StopIteration
self.idx += 1
return nxtitem
next = __next__ # py2 compat
list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
for i in CustomIterator(list_A):
print(list_A.pop())
if 'c' not in list_A:
break
打印哪些:
give next element: 0
0 ---> a
1 b
2 c
3 d
4 e
5 f
6 g
7 h
8 i
9 j
j
give next element: 1
0 a
1 ---> b
2 c
3 d
4 e
5 f
6 g
7 h
8 i
i
give next element: 2
0 a
1 b
2 ---> c
3 d
4 e
5 f
6 g
7 h
h
give next element: 3
0 a
1 b
2 c
3 ---> d
4 e
5 f
6 g
g
give next element: 4
0 a
1 b
2 c
3 d
4 ---> e
5 f
f
give next element: 5
0 a
1 b
2 c
3 d
4 e
所以它并没有因为break
而结束,而是因为它遍及整个列表(或更好:直到没有更多的项目!)。
此外'c' not in listA
是O(n)
操作,因此您的循环有效O(n**2)
。为什么不找到'c'
的第一个索引并简单地迭代直到你在那里:
list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
try:
c_index = list_A.index('c')
except ValueError:
# no 'c' in the list, probably should do something more useful here ...
pass
else:
for item in reversed(list_A[c_index:]): # print the items
print(item)
del list_A[c_index:] # remove the items from the list
打印(按预期):
j
i
h
g
f
e
d
c
答案 3 :(得分:1)
list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
while list_A: # while list_A has elements, in case 'c' wasn't present
el = list_A.pop() # save the last element
print(el)
if 'c'==el: # if 'c' was popped (reached)
break
print("job done.")
这样,即使'c'
不存在,它也会打印所有内容然后退出。这也避免了检查每次迭代是否存在'c'
,这需要时间。
基于@ MSeifert的评论,如果循环不应该在弹出的第一个c
停止而是在列表没有c
时停止,则对代码进行一点修改以上结果:
list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'c', 'h', 'i', 'j']
while list_A:
print(list_A.pop())
if 'c' not in list_A:
break
print("job done.")
我们可以走得更快,但我不知道你是否学会了列表切片和补偿,所以这是一个更好,更快的解决方案:
list_A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
try:
p=list_A.index('c')
r='\n'.join(list_A[list_A.index('c'):][::-1])
except ValueError:
r='\n'.join(list_A[::-1])
print(r)
print('job done.')