LINUX.ORG.RU

Python, как удалить элемент в списке во время перебора?

 , , ,


1

2

Задача: есть список, надо перебирать в нём элементы по кругу, и (время от времени) удалять оттуда текущий элемент прямо во время перебора

Ниже моя неудачная попытка это сделать.

Есть код:

from itertools import cycle
import time

m = [1,2,3]

for i in cycle(m):
    time.sleep(0.3)
    print(i)
    # m.remove(i)

Он выдаёт числа по кругу:

1
2
3
1
2
3
1
2
3
итд

Теперь надо как-то к примеру во время работы удалить например число 2, и чтобы далее список продолжил перебираться между 1 и 3.



Последнее исправление: rubro (всего исправлений: 1)

Ответ на: комментарий от panter_dsd

Спасибо за идею! Я немного модифицировал код:

import pdb

a = [1, 2, 3]
while a:
    for i in list(a):
        print(i)
        pdb.set_trace()
    # remove element from a

Далее заметил такой расклад:

1
> c:\demo_cycle.py(24)<module>()
-> for i in list(a):
(Pdb) del a[1]
(Pdb) continue
2
> c:\demo_cycle.py(24)<module>()
-> for i in list(a):
(Pdb) continue
3
> c:\demo_cycle.py(24)<module>()
-> for i in list(a):
(Pdb) continue
1
> c:\demo_cycle.py(24)<module>()
-> for i in list(a):
(Pdb) continue
3

Выходит если во время выполнения удалить элемент, то далее скрипт всё равно будет думать что этот элемент существует, и эта «память» держится до конца текущий итерации (в следующей уже всё нормально). Порешать бы это как-то. Если удалили элемент - скрипт должен тут же это увидеть.

rubro
() автор топика
Ответ на: комментарий от panter_dsd

Спасибо, правда я немного не понял код, он всего лишь 1 раз итерирует.

В общем написал свой говнокод, выглядит вот так:

import time
import random

m = [1,2,3]

x = 0
while m:
    time.sleep(0.3)

    print(m[x])

    # с вероятностью в 20% удаляем текущий элемент
    if random.random() < 0.20:
        print('удалили: {}'.format(m[x]))
        del m[x]

    x += 1
    if x >= len(m):
        x = 0
Вот пример вывода:
1
2
3
1
удалили: 1
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
удалили: 2
3
3
3

@panter_dsd, спасибо за помощь!

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

rubro
() автор топика
Ответ на: комментарий от rubro

В конце там: удалили: 3

И далее успешный выход

rubro
() автор топика
Ответ на: комментарий от panter_dsd

Вообще ничего странного, я закидываю в пул к примеру 100 заданий, они выполняются асинхронно, далее по кругу проверяю те что уже готовы (успешные), их дополнительно обрабатываю (пишу результат в базу) и надо теперь их удалить из списка и продолжить остальные проверять до тех пор пока список не будет пуст (пока все не будут готовы).

Вроде вполне типичная задача.

rubro
() автор топика
Ответ на: комментарий от panter_dsd

Не могу ваш код завести(((

a = [1, 2, 3]
while a:
    for i in range(len(a)):
        if i < len(a):
            # work with a[i]
            print(a[i])
    else:
        break
Вывод:
1
2
3

Даже не пробует ходить по кругу

rubro
() автор топика
Ответ на: комментарий от Dark_SavanT

Туплю, не могли бы вы пожалуйста показать как это выглядит?

rubro
() автор топика
Ответ на: комментарий от panter_dsd

Не удаётся построить из кода простой пример, не могли бы вы пожалуйста запилить простой сэмпл где допустим удалять или нет из списка определялось бы по вероятности? Как в том коде что я выше приводил в пример

rubro
() автор топика
Ответ на: комментарий от panter_dsd

Отлично, код завёл, выглядит вот так:

a = [1, 2, 3]
while a:
    time.sleep(0.3)
    for i in range(len(a)):
        if i < len(a):
            # work with a[i]
            print(a[i])
            # # с вероятностью в 20% удаляем текущий элемент
            if random.random() < 0.20:
                print('удалили: {}'.format(a[i]))
                del a[i]
        else:
            break
Только очень не нравится эта вложенность глубокая, как-то от неё можно избавиться? Сначала тут идёт while, потом for, затем if... Слишком много уровней, в этом плане мой код был поудобней немного

rubro
() автор топика
Ответ на: комментарий от panter_dsd

Да пока что не требуется на пул это всё натягивать, необходим простой пример где за основу взят будет код вероятности в 20%:

if random.random() < 0.20:
    # do stuff

То есть элемент пусть удаляется из списка в 20% случаев.

Когда простой пример будет в кармане, на пул я его уже сам натяну

rubro
() автор топика
Ответ на: комментарий от panter_dsd

Так понятнее будет. Обожаю пайтон за такое.

def can_delete(value):
  result = random.random() < 0.20
  if result:
    print('Delete!!!')
  return result

while a:
  a = [i for i in a if not can_delete(i)]
panter_dsd ★★★★
()
Ответ на: комментарий от rubro

Вообще не принято менять параметр цикла внутри него же. Это вообще черевато. Обычно список в таком случае пересобирают каждый раз. Например

while a:
    a = [x for x in a if random.random() < 0.20]
    for x in a:
        print(x)

Dred ★★★★★
()
Ответ на: комментарий от Dred

Немного для меня запутанный код, не могли бы вы пожалуйста модифицировать его так чтобы было видно текущую итерацию? А то он работает так что я не вижу что происходит внутри

rubro
() автор топика
Ответ на: комментарий от rubro

читай про list comprehension. Тут происходит следующее: на каждой итерации строится новый список без элементов не попавших под условие, а потом над ними выполняются действия.

Dark_SavanT ★★★★★
()
Ответ на: комментарий от rubro

Кстати а чем черевато то?

непредсказуемым поведением например. Тут частенько возникают темы из разаряда «Почему удалял элементы списка внутри цикла и поймал выход за границы»

а вот как текущую итерацию вывести через print() не ясно

эмм, я специально добавил вывод списка на экран

    for x in a:
        print(x)
Какую еще итерацию надо выводить ?

Dred ★★★★★
()
Ответ на: комментарий от Dred

Мне кажется если использовать пример выше от коллеги @panter_dsd то всё ровно должно быть, разве нет? Этот пример архитектурно я не планирую меняь и он выглядит надёжным.

Какую еще итерацию надо выводить ?

По каким-то причинам ваш скрипт выводит либо пустоту, либо 1 число из списка, я ожидал вывод в логах как было в примерах выше, то есть выводится текущая итерация и постепенно список «худеет» и в итоге скрипт выходит.

rubro
() автор топика
Ответ на: комментарий от panter_dsd

Да точно, ваш пример почти такой же, я не обратил внимание сразу мне показалось он по другим принципам построен, большое спасибо ещё раз! Всё кратко, красиво, компактно, всё в лучших традициях пЯтона

rubro
() автор топика
Ответ на: комментарий от Dred

Да, немного глаза замылились, большое спасибо за участие и помощь!

rubro
() автор топика
Ответ на: комментарий от rubro

Этот пример архитектурно я не планирую меняь и он выглядит надёжным.

особенно учесть что они практически одинаковы

выводит либо пустоту, либо 1 число из списка,

Вероятно стоит сменить условие на подходящее ? Я-то просто для примера случайное запихал. Или вы код не разбираясь запускаете ?

то есть выводится текущая итерация

либо вы не понимаете как работают циклы, либо мы говорим о разных штуках. ИМХО, лучше вообще выводить print(a) (это если там не сильно большой list, конечно) вместо прохода по циклу, ибо так нагляднее

Dred ★★★★★
()
Ответ на: комментарий от Dred

Отвечать не буду так как это всё уже разрулено, ещё раз большое спасибо за помощь!

rubro
() автор топика
Ответ на: комментарий от pod

Думал об этом, но показалось оно говнокодом будет, это надо сначала взять из очереди, потом обратно положить. Некрасиво же.

rubro
() автор топика

Питона не знаю. Но у тебя проблема с конкурентной синхронизацией. Надо гуглить Pythob mutex Python lock Python concurrency synchronization Что-то в этом вроде

nikolnik ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.