List is not working as expected

Hi Everyone,

I am quite new in Python so probably an error is on my side, but I am not able to find out what is wrong:

# using c to have list of files in dir = 582 elements total
for a,b,c in os.walk('c:\\some\\path\\'):
    pass
.
#some script - also removing elements from list here
.
.
print(c)
print("Keeping prefixes: pref1, pref2, pref2, pref4, pref5, pref6")
pref_list =["pref1","pref2","pref3","pref4","pref5","pref6"]
for entry in c:
    for pref in pref_list:
        print("dump:  ", entry, "  ", pref)
        if pref == entry[:len(pref)]:
            c.remove(entry)
            print(entry, " not moved - prefix matched")

So I have list of prefixes and if file contains this prefix its removed from the list.
Once all files are matched again prefixes code continue to move rest of the files
in list to another directory.

Problem is that print(c) is showing also lists elements which for entry in c:
doesn’t loop through. I know it because print("dump: ", entry, " ", pref) should
print all loops in those 2 cycles. I tried to use sort() function, but some elements
which match prefix does not run in loop even they are visible in print(c).

Can anyone advise me what I am doing wrong ?

Thanks

btw - I have correct formating - not sure how to setup it here

Howdy Thomas,

the forum editor has a control which looks alike “</>” (“preformatted text”) - if you select your code and press said control after that, the lines will be indented properly - otherwise it is quite hard or at least unpleasant to read python code… Could you be so kind?

Cheers, Dominik

Hi Tomas,

Your problem is that you are removing items from the list as you are
iterating over it. That is always a recipe for trouble unless you are
very, very careful about how you do it.

Here is a simplified example:

>>> L = ['a', 'b', 'c', 'd', 'e', 'f']
>>> for position, char in enumerate(L):
...     print('Position:', position, 'char:', char, 'L =', L)
...     if char in 'bcde': L.remove(char)
... 
Position: 0 char: a L = ['a', 'b', 'c', 'd', 'e', 'f']
Position: 1 char: b L = ['a', 'b', 'c', 'd', 'e', 'f']
Position: 2 char: d L = ['a', 'c', 'd', 'e', 'f']
Position: 3 char: f L = ['a', 'c', 'e', 'f']

What happens is that when the ‘b’ at position 1 is removed, that pushes
all the rest of the letters forward one slot towards the front of the
list. So the ‘c’ moves into the position 1, but that position has
already been looked at. So the next iteration moves to position 2, which
has a ‘d’, and the ‘c’ never gets seen.

There are many ways to solve this, but the easiest is to iterate over a
copy of the list:

>>> L = ['a', 'b', 'c', 'd', 'e', 'f']
>>> for char in L[:]:  # iterate over a copy of L
...     if char in 'bcde': L.remove(char)
... 
>>> L
['a', 'f']

In addition to Steven D’Aprano excellent answer: there is also list comprehension which has benefit of not creating copy but it returns new list instead of modifying original one. Algorithm in natural language: ‘give me character for every character in list which is not b, c, d or e’ and Python implementation of it:

>>> list_ = ['a', 'b', 'c', 'd', 'e', 'f']
>>> [char for char in list_ if char not in 'bcde']
['a', 'f']

Hi,

Thanks for all answers - I have to agree with Aivar about Steven excellent answer - it make me understand what I did wrong and I will use copy of list as advised.

Thanks and have a nice day !