Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to loop through a set, while removing items from the set in Python 3

Here is my situation:

I have a list/set (doesn't matter which) of movieplayer objects that I want to call a "preload" function on. This preload function could return immediately but would like return a bit in the future.

I want to store this collection of movieplayers, indicating that they have not been preloaded yet, then loop through them, calling the preload function. The preload function, when returned, would remove them from the collection (so I would know when they are all preloaded).

However, I think because python is waiting for the preload function, then removing the player, I am getting a set size changed during iteration error.

Hey is a simplified version of my code, I would appreciate a way to navigate this issue.

a = set([mp1, mp2 mp3])
for player in a:
    preload(player)

# preload would be something like
def preload(player):
    player.preloadVideo()
    a.remove(player)
    # This is where I believe the error gets generated.

The only solution that I can think of would be to make a copy of the set a, and then iterate through that, but I am not sure if that is the right way to do it or would even work.

like image 717
Startec Avatar asked Sep 02 '26 05:09

Startec


2 Answers

You can fix it by iterating over a copy of the set. I've used list() here:

a = set(['mp1', 'mp2', 'mp3'])

# preload would be something like
def preload(player):
    player.preloadVideo()
    a.remove(player)

for player in list(a):
    preload(player)    # N.B. pass player, not a

This, however, is not a great design. For one thing the global variable, a is referenced from within the preload() function. Furthermore, the for loop iterates over all elements of the set passing each in turn to preload(), so it is not necessary to check membership of each player in a. Lastly, preload() should perform whatever is required to preload the player, but it should not be responsible for maintaining an external data structure (a).

A better design is for preload() to return a boolean indicating whether the preload was successful or not. Removal of a successfully loaded player can then be done outside of the preload function:

a = set(['mp1', 'mp2', 'mp3'])

# preload would be something like
def preload(player):
    return player.preloadVideo()    # assume that this returns boolean

for player in list(a):
    if preload(player):
        a.remove(player)

if len(a):
    print "{} player(s) failed to preload: {}".format(len(a), a)        
else:
    print "All players successfully preloaded"

This code will only remove a player once it has been successfully preloaded.

like image 83
mhawke Avatar answered Sep 03 '26 19:09

mhawke


There is no need to loop over your list 2 time! as you said, it will raise a size changed error.So instead you can use pop property of set and list to get those items which return the value and remove it from your data structure.For a list you can pass the 0 index to pop in each iteration also as a more pythoinc way you can use while instead of for when you want to remove item from your data structure :

a = [mp1, mp2 mp3]
while a:
    preload(a.pop(0))

But for set the pop doesn't accept the index:

a = set([mp1, mp2 mp3])
while a:
    preload(a.pop())

Example :

>>> def preload(i):
...   print i
... 
>>> a=[1,2,3]
>>> while a:
...     preload(a.pop(0))
... 
1
2
3
>>> a
[]
>>> a={1,2,3}
>>> while a:
...     preload(a.pop())
... 
1
2
3
>>> a
set([])
like image 23
Mazdak Avatar answered Sep 03 '26 19:09

Mazdak



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!