Python Forum
.remove() method in for-loop
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
.remove() method in for-loop
#1
it is clear what this code do:
>>> sw = ['go', 'bo']
>>> for cm in sw:
... 	if cm == 'bo':
... 		sw.remove(cm)
... 
>>> sw 
['go'] 
but when a list contains 4 'bo' items only 2 will be removed:
>>> sw = ['go', 'bo', 'bo', 'bo', 'bo']
>>> for cm in sw:
... 	if cm == 'bo':
... 		sw.remove(cm)
... 
>>> sw 
['go', 'bo', 'bo']
>>>  
why not all 'bo' items are removed?
Reply
#2
Because you are changing the list as you are looping over it. When it goes to get the fourth item, there's only three items left. In cases like this you would either loop through a copy of the list (for cm in sw[:]:), or you would use a while loop:

while 'bo' in sw:
    sw.remove('bo')
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#3
Why not doing like this?
>>> sw = ['go', 'bo', 'bo', 'bo', 'bo']
>>> for i in range(0, sw.count("bo")):
...     sw.remove("bo")
... 
>>> sw
['go']
Well, ichabod was faster and more clever using while-loop
Reply
#4
Or you could do a list comprehension:

sw = [item for item in sw if item != 'bo']
For large lists, that would probably be more efficient. Filter might be even more efficient.
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#5
(Oct-08-2018, 08:30 PM)TimeMen Wrote: Why not doing like this?
If think about it's about 8-9 function call for that short list,without running profile on it.

ichabod80 Wrote:Or you could do a list comprehension:
It's about as pythonic as it get.
It work well in most cases small or big list.

I think set() can be fast if have larger list.
>>> sw = ['go', 'bo', 'bo', 'bo', 'bo']
>>> set(sw) - set(['bo'])
{'go'}
Can do some measuring with timeit.
import timeit

def time_1(lst):
    for i in range(0, lst.count("bo")):
        lst.remove("bo")

def time_2(lst):
    sw = [item for item in lst if item != 'bo']

def time_3(lst):
    set(lst) - set(['bo'])

def time_4(lst):
    while 'bo' in lst:
        lst.remove('bo')

lst = ['time_1', 'time_2', 'time_3', 'time_4']
for test in lst:
    t = timeit.Timer(f"{test}(['go', 'bo', 'bo', 'bo', 'bo'])", f'from __main__ import {test}').timeit(number=10000000)
    print(f'{test} --> {t:.2f}')
Output:
time_1 --> 13.59 time_2 --> 7.83 time_3 --> 9.11 time_4 --> 10.88
Now make list bigger *100,and run it a little less average iteration number=100000.
Now see that set() shine 0.87,and list comprehension still okay with 2.16
Output:
time_1 --> 48.71 time_2 --> 2.16 time_3 --> 0.87 time_4 --> 87.82
Reply
#6
Yes, but set will change the order and remove duplicates. That may be desired behavior. And you didn't test filter. If you add

def time_5(lst):
    sw = filter(lambda item: item != 'bo', lst)
Then the short list times are:

Output:
time_1 --> 9.78 time_2 --> 4.95 time_3 --> 6.95 time_4 --> 7.73 time_5 --> 3.40
And the long list times are:

Output:
time_1 --> 4.09 time_2 --> 0.18 time_3 --> 0.07 time_4 --> 7.32 time_5 --> 0.03
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#7
(Oct-09-2018, 01:04 AM)ichabod801 Wrote: And you didn't test filter. If you add
Yes the filter one is fast.
Can also do a run PyPy,
It take a lot time down on the solution that not so optimal and not so much(as expected) on solution that is already good.
# Python 3.7
λ python test_bo.py
time_1 --> 53.79
time_2 --> 2.16
time_3 --> 1.03
time_4 --> 92.84
time_5 --> 0.20

# PyPy Python 3.5
λ pypy3 test_bo.py
time_1 --> 17.54
time_2 --> 0.76
time_3 --> 0.93
time_4 --> 21.62
time_5 --> 0.15
Reply
#8
You can avoid a lot of problems when removing items from a list if you traverse it in reverse:

sw = ['bo', 'go', 'bo', 'do', 'bo', 'we', 'bo', 'bo']
for cm in sw[::-1]:
    if cm == "bo":
        sw.remove("bo")
print(sw)
['go', 'do', 'we']
I am trying to help you, really, even if it doesn't always seem that way
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020