使用for循环遍历一个python列表的同时,使用append方法向列表中追加元素,这个for循环还能正常执行么?在元素pip源码时,我在legacy_resolve.py 脚本中注意到了resolve这个函数,函数定义如下
def resolve(self, requirement_set):
root_reqs = (
requirement_set.unnamed_requirements +
list(requirement_set.requirements.values())
)
discovered_reqs = [] # type: List[InstallRequirement]
hash_errors = HashErrors()
for req in chain(root_reqs, discovered_reqs):
try:
discovered_reqs.extend(self._resolve_one(requirement_set, req))
except HashError as exc:
exc.req = req
hash_errors.append(exc)
if hash_errors:
raise hash_errors
我们不去关注函数的具体功能,注意看discovered_reqs.extend(self._resolve_one(requirement_set, req)) 这样代码,在使用for循环遍历discovered_reqs的同时使用append方法向列表追加元素,难道不会造成什么异常么?
这是一个非常有趣的问题,在讨论这个问题之前,先来看看如果使用remove方法从列表里删除一个元素会怎样
lst = [1, 3, 2, 5, 7, 9]
for i in lst:
if i % 2 == 1:
lst.remove(i)
print(lst)
这段代码的意图是删除列表里的奇数,然而lst最终的内容却是 [3, 2, 7],产生这个现象的原因是for循环的本质是使用迭代器进行遍历,删除迭代器刚刚访问过的元素,下一次迭代中,迭代器将向后跳转1个元素。删除元素1时,迭代器向右跳转1个元素,刚好越过了3,remove影响了迭代器的正常使用。
如果在遍历列表时使用append向列表中添加元素,会是怎样的效果呢,这里的关键是append方法是否会影响到for循环所使用的迭代器,如果append方法向列表追加元素后同时修改迭代器且不破坏迭代器,那么就不会影响迭代器的行为,下面是一段实验代码
lst = [1, 2, 3]
for item in lst:
if item % 2 == 0:
print(item)
lst.append(item * 2)
if item > 20:
break
实际的输出结果是
2 4 8 16 32
从程序输出的结果进行推理,append方法改变了迭代器,for循环遍历到整数2时,向列表末尾追加了整数4, 遍历整数3结束后,仍然进入下一次循环,这说明for循环所使用的迭代器可以识别到列表中新加入的元素。
换一种方法来验证上面的推理
lst = [1, 2, 3]
tmp_iter = iter(lst)
print(next(tmp_iter))
print(next(tmp_iter))
print(next(tmp_iter))
lst.append(4)
print(next(tmp_iter))
QQ交流群: 211426309