For loop touble

So i’m back with a new question this time about a for loop not doing what it needs to do.

i use this

if own['libraries']:

            print('*before**\n' + str(own['libraries']) + '\n***')
            
            for obj in own['libraries']:
                print('looped trough: '+obj)
                logic.LibFree(obj)
                own['libraries'].remove(obj) 

            print('*after**\n' + str(own['libraries']) + '\n***')    

and get this error

Python script error - object 'assets_loader', controller 'Python':
Traceback (most recent call last):
  File "D:\_blender\__Fps project\levels\level_0.Blend\assets_loader.py", line 43, in load
ValueError: blend file already open "D:\_blender\__Fps project\assets\asset_1.Blend"

This are the print results

*before**
['D:\\_blender\\__Fps project\\levels\\level_0.Blend', 'D:\\_blender\\__Fps project\\assets\\asset_1.Blend', 'D:\\_blender\\__Fps project\\assets\\asset_2.Blend', 'D:\\_blender\\__Fps project\\assets\\asset_3.Blend']
***
looped trough: D:\_blender\__Fps project\levels\level_0.Blend
looped trough: D:\_blender\__Fps project\assets\asset_2.Blend

*after**
['D:\\_blender\\__Fps project\\assets\\asset_1.Blend', 'D:\\_blender\\__Fps project\\assets\\asset_3.Blend']
***

Now my question is why does it not delete/free all that is in the list?
It loops trough 2 entries then it aborts and starts loading a new blend, this results in the error above, due to yes indeed, the blend has not been freed so it’s still loaded.

Always the same, ask a question and solve it right after i asked.

SOLVED by using while own[‘libraries’]:
now it properly loops trough all

Explanation for those who want to know:

When you do a for loop on a list, make sure the list is not changing.

Another fix would be:

libraries = owner['libraries']

# make a copy that will not change during the looping
for lib in libraries.copy():
    logic.LibFree(lib)
    libraries.remove(lib)

# or you can use the slice notation, this does a copy too:
for lib in libraries[:]:
    logic.LibFree(lib)
    libraries.remove(lib)

# or if you are removing the element you are visiting,
#  you can do a while loop.
# because eventually you will remove every element,
#  the while loop will stop then.
while libraries:
    logic.LibFree(libraries.pop())

But this weird behaviour happens because LibFree seems to remove items from the reference to the actual list returned by logic.LibList. I didn’t know that.

Ok in fact it is the list you created yourself that you mess up. So yeah, morale: don’t change the list while iterating.

I’d be tempted to do something like:

if own['libraries']:
  while len(own['libraries']):
    logic.LibFree(own['libraries'].pop())

The loop will continue as long as there is something in the list. The pop() will take an element out of the list and put it through LibFree(). Once the list is empty, the while loop will finish.

-edit-
Oops, WKnight02 beat me to that with his last suggestion.

indeed i could jave tried .pop, maybe that works as well, for this problem the while loops seems to work for what i want, but now i’m very confused, because it will load the wrong assets.

I will go back to my other topic with this problem.

you could take a look at deque from the collections module.

here is a small test where i use it to rotate a list of objects.

import collections
   
def Rotate(_list,n=1):
    tmp = collections.deque(_list)
    tmp.rotate(n)
    return list(tmp)


l = [{str(c):c} for c in range(10)]

print(l)

for i in range(10):
    l = Rotate(l,-1)
    print("\n--- rotate < ",i+1,"----------------")
    print(l)