我有大约200 000多个对象的列表,每一个代表一个文件(但并不实际持有该文件的内容,不仅仅是使用全路径名和日期)。
我正在编写的程序会复制这些文件的任何子集,具体取决于用户提供的日期范围。我首先创建所有在源目录中的文件的列表(与该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...
示例出现该问题:我被抛出的IndexError时的值i
到达63792
IndexError: list index out of range.
有什么想法吗?
编辑非常感谢您的快速回复!现在我想起来,这对我来说是一个愚蠢的疏忽。再次,谢谢大家。 :)
答案 0 :(得分:7)
来自the docs:
修改循环中迭代的序列是不安全的(这只能发生在可变序列类型中,例如列表)。如果您需要修改要迭代的列表(例如,复制所选项目),则必须迭代副本。
对于您的情况,我实际上建议使用生成器表达式和itertools.ifilter
,以避免为您的大文件列表制作不必要的副本。
答案 1 :(得分: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)
这可能会更加pythonic,但应该足够可读,以获得重点。
答案 2 :(得分:2)
[编辑] 哎呀,我忘了颠倒“&lt;”和“&gt;”并添加一个'等于'标志。
LOG_FILES = [LogFile(f) for f in glob.glob(srcdir + "/*.txt")
if from_date <= f.DATE <= to_date]
这可以取代LOG_FILES的整个初始化。这是一个列表理解(如果你希望你可以把它作为一个生成器(在枚举之前不会被评估),可以用[]替换()。这可能会更有效,这取决于你用它做什么。
您需要这样做,因为不允许在枚举时编辑集合。 (见上文,far more eloquent答案)。
您可以像这样阅读上面的表达式:
“创建一个LogFile结果的列表(或可枚举),当它在'glob.glob(...)'中为每个f传递'f'时,但仅当'if'语句为真。”< / p>
请参阅该链接的The List Comprehension部分。
答案 3 :(得分:1)
如果您在具有固定上限的数组上循环并同时删除元素,将生成索引错误。您必须循环复制或使用动态索引。既然你说数组很大,我们使用后者:
limit, i = len(LOG_FILES), 0
while i < limit:
if LOG_FILES[i].DATE < from_date and LOG_FILES[i].DATE > to_date:
del(LOG_FILES[i])
limit -= 1
else:
i += 1
答案 4 :(得分:1)
您也可以使用filter
:
LOG_FILES = filter(lambda log_file: log_file.DATE < from_date and \
log_file.DATE > to_date, LOG_FILES)
答案 5 :(得分:1)
Cpfohl的回答有一个问题:
LOG_FILES = [LogFile(f) for f in glob.glob(srcdir + "/*.txt")
if f.DATE >= from_date and f.DATE <= to_date]
由于
for f in glob.glob(srcdir + "/*.txt"):
LOG_FILES.append(LogFile(f))
因此LOG_FILES [i]是LogFile(f) 然后LOG_FILES [i] .DATE是一个LogFile(f).DATE,而不是f.DATE
答案 6 :(得分:0)
1)在列表中从头到尾的迭代中删除元素解决问题
LOG_FILES = [ 1,2,30,2,5,8,30,3,2,37,22,30,27,30,4 ]
print LOG_FILES
L = len(LOG_FILES)-1
for i,x in enumerate(LOG_FILES[::-1]):
print i,L-i,' ',LOG_FILES[L-i],x
if x>15:
del LOG_FILES[L-i]
print LOG_FILES
结果
[1, 2, 30, 2, 5, 8, 30, 3, 2, 37, 22, 30, 27, 30, 4]
0 14 4 4
1 13 30 30
2 12 27 27
3 11 30 30
4 10 22 22
5 9 37 37
6 8 2 2
7 7 3 3
8 6 30 30
9 5 8 8
10 4 5 5
11 3 2 2
12 2 30 30
13 1 2 2
14 0 1 1
[1, 2, 2, 5, 8, 3, 2, 4]
2)顺便说一下
if LOG_FILES[i].DATE < to_date and LOG_FILES[i].DATE > from_date :
可以写
if from_date < LOG_FILES[i].DATE < to_date: