2012-01-05 2 views
1

При попытке удалить все имена файлов не начинающиеся с определенной подстрокой из списка, я столкнулся следующим неожиданное поведение:list.remove() не обеспечивает ожидаемые результаты

>>> allfiles = os.listdir(mydir) 
>>> allfiles 
['dwcpybyext.sh', 'dwlaunch', 'libupdate.sh', 'ntpsync.sh'] 
>>> for f in allfiles: 
... if f.startswith('n') == False: 
...  allfiles.remove(f) 
... 
>>> allfiles 
['dwlaunch', 'ntpsync.sh'] 

Это теоретически должно иметь удален каждые имя файла не начинается с 'n' из списка. Вместо этого он остается одним, начиная с 'd' в списке. Если я меняю цикл на использование if f.startswith('d') == False:, я получаю ['dwcpybyext.sh', 'dwlaunch', 'ntpsync.sh'] - последний элемент даже не содержит символ 'd'.

Почему я вижу это поведение? Вероятно, это ошибка в методе list.remove() Python. Я получаю такое же поведение, если подставляю del allfiles[allfiles.index(f)], а .remove() - это в основном просто псевдоним для этого.

+1

Читайте примечание в разделе 7.3: http://docs.python.org/reference/compound_stmts.html #for –

ответ

7

Это очень плохая идея для изменения списка при его повторении. Попытайтесь совершить следующее:

allfiles = filter(lambda x: x.startswith('n'), allfiles) 
+7

Я бы воспользовался списком, который яснее, и возвращает список в Python 3. '[x для x в allfiles, если x.startswith ('n')]' – kennytm

+0

@KennyTM Этот выбор зависит от OP –

+0

Понимание списка - лучший метод для моих нужд. Спасибо за помощь! – Kudzu

0

Вы изменяете тот же список, в котором вы итерации.

Вы должны либо скопировать список или перебирать его в обратном порядке и с использованием индексов для доступа к списку, так что предыдущий индекс всегда существует

example = ['a','b','c'] 
for i in reversed(range(len(example))): 
    if example[i] == 'b': 
     del(example[i]) 
2

Вы не должны изменять список вы итерацию. Использовать

allfiles = [f for f in allfiles if f.startswith('n')] 

вместо этого.

Update: Малый сравнение производительности с filter альтернативы по @RomanBodnarchuk (что совершенно нормально, конечно):

$ python -mtimeit -s'L=range(10000)' '[x for x in L if x < 100]' 
1000 loops, best of 3: 662 usec per loop 
$ python -mtimeit -s'L=range(10000)' 'filter(lambda x: x < 100, L)' 
100 loops, best of 3: 2.06 msec per loop 

кажется списочные быстрее, чем filter с lambda с коэффициентом 3

+0

Спасибо за дополнительную информацию. В этом случае скорость * * важна, поэтому это еще одна причина использовать понимание списка. – Kudzu

1

Не знаю точно, но, вероятно, имеет отношение к тому факту, что вы изменяете список, который вы повторяете! Не делай этого. Вместо этого следует использовать такой код:

allfiles = [f for f in os.listdir(mydir) if f.startswith('n') 

или, если вы предпочитаете цикл:

allfiles = [] 
for f in os.listdir(mydir): 
    if f.startswith('n'): 
     allfiles.append(f) 
Смежные вопросы