Python在遍历大列表时出现IndexError

3 投票
7 回答
758 浏览
提问于 2025-04-16 10:20

我有一个大约20万个对象的列表,每个对象代表一个文件(但并不包含文件的内容,只是文件的完整路径和日期)。

我正在编写的程序会根据用户提供的日期范围复制这些文件的任意子集。首先,我会创建一个源目录中所有文件的列表(使用glob模块),然后创建一个我的文件表示类的实例,并把这个实例添加到一个列表中,像这样:

for f in glob.glob(srcdir + "/*.txt"):
    LOG_FILES.append(LogFile(f))

接下来,为了让文件复制更快,同时保持代码块的整洁,我会移除那些不在日期范围内的LogFile对象。

for i in xrange(0, len(LOG_FILES)):
    if LOG_FILES[i].DATE < from_date or LOG_FILES[i].DATE > to_date:
        del(LOG_FILES[i])

之后,我只需要复制列表中剩下的文件:

for logfile in LOG_FILES:
    os.copy(logfile.PATH, destdir)

问题出现在for i in xrange...这个例子中:当i的值达到63792时,我遇到了一个IndexError错误。

IndexError: list index out of range.

有什么想法吗?

编辑 非常感谢大家的快速回复!现在想想,这真是我一个小小的疏忽。再次感谢大家。:)

7 个回答

3

你这个方法的问题在于,del() 是在删除列表中某个位置的元素,同时还会把后面的元素往前移动。

举个例子,如果你的列表里有五个元素,当你在第三个位置调用 del() 时,列表里的内容会往下移动,这样就会有一个不同的元素占据第三个位置。

list = [1,2,3,4,5]
del(list[2])
print list     # outputs [1, 2, 4, 5]
print list[2]  # outputs 4

因为你是从0循环到列表的原始大小,即使你只删除了一个元素,最终你也会到达一些在列表中已经不存在的索引。

一个更简单的方法是,在你添加元素的时候就对列表进行过滤。

for f in glob.glob(srcdir + "/*.txt"):
    lf = LogFile(f)
    if lf.DATE < from_date and lf.DATE > to_date:
        LOG_FILES.append(lf)

这个方法可能可以更符合 Python 的风格,但应该足够清晰,让你明白要点。

7

来自官方文档

在循环中,不安全地修改正在遍历的序列(这只会发生在可变序列类型,比如列表)。如果你需要修改正在遍历的列表(比如,复制选中的项目),你必须遍历一个副本。

对于你的情况,我建议你可以看看生成器表达式和itertools.ifilter,这样可以避免不必要地复制你那大堆文件的列表。

2

[编辑] 哎呀,我忘了把"<"和">"反过来,并加上一个'等于'号。

LOG_FILES = [LogFile(f) for f in glob.glob(srcdir + "/*.txt")
                        if from_date <= f.DATE <= to_date]

这段代码可以替代LOG_FILES的整个初始化过程。它使用了一种叫做列表推导式的写法(如果你愿意,可以把方括号[ ]换成圆括号( ),这样就变成了生成器,只有在你需要的时候才会计算,这样可能会更高效,具体要看你怎么使用它)。

你需要这样做,因为在遍历一个集合的时候是不能修改它的。(具体可以参考上面的链接,那里有更详细的解释)。

你可以这样理解上面的表达式:

“创建一个列表(或者可遍历的对象),内容是LogFile的结果,当它接收到'f'时,针对'glob.glob(...)'中的每个'f',但只有在'if'条件成立的情况下。”

详情请见:这个链接中的列表推导式部分。

撰写回答