среда, 11 июля 2012 г.

list и list.remove() - внимание


Дописывая свою программу для отображения задач из Lightning, долго не мог понять (вообще получить по этой теме информацию, кроме как читать исходники не возможно... а в С я не в зуб ногой, тем более такого объёма), как удалять из вывода повторяющиеся задачи. Путём длительных алхимических изысканий вроде как понял, что за это отвечает поле flags из таблицы cal_todos. Это битовая маска,5ый бит которой вроде и означает повторяющуюся задачу. Соответственно код
for t in tasks:
    elif t.flags & 16 :
        tasks.remove(t)
должен был спасти отца русской демократии. Но не тут то было...

Как назло одна задача, которая должна была отфильтроваться никак не фильтровалась. Не буду интриговать - сразу скажу в чём дело. Давайте посмотрим на этот код:
# тут два значения, которые должны отфильтроваться стоят подряд
>>> for k in (4,20,262,276,308,260):
 print (k&16)
 
 
0
16
0
16
16
0
# возьмём значения в списке:
>>> a=[4,20,262,276,308,260]
>>> for k in a:
 if k&16: a.remove(k)
 
# посмотрим, что осталось после фильтрации 
>>> a
[4, 262, 308, 260]
# опаньки, 308-то осталось...
# и вот почему...
>>> a=[4,20,262,276,308,260]
>>> for k in a:
 if k&16: a.remove(k)
 print (k,a)
 
 
(4, [4, 20, 262, 276, 308, 260])
(20, [4, 262, 276, 308, 260])
(276, [4, 262, 308, 260])
(260, [4, 262, 308, 260]) 

То есть, итерация по списку видимо идёт при помощи указания номера элемента, и когда мы удаляем текущий элемент - следующий элемент этот указатель проскакивает (так как 308 становится 3им элементом, а указатель показывает на 4ый...)
Так что правильный код для меня вот такой:
tasks_end=[]
for t in tasks:
    if t.flags & 16 :
        tasks.remove(t)
    else:
        tasks_end.append(t)

То есть надо просто добавлять элементы в другой список. Век живи - век учись)


UPD Vaal предложил такой вариант, гораздо более красивый:

a = [4, 20, 262, 276, 308, 260]
 
for k in a[:]:
    if k & 16:
        a.remove(k)
 
print(a)

2 комментария: