While writing my game, I've been using Python lists to keep track of things on the screen like laser shots and asteroids. On-screen objects typically have limited lifetimes and then get deleted from their associated list. This leads to code that looks something like the following.import randomclass Foo(object): def __init__(self): self.time = random.randint(1, 100) def update(self): self.time -= 1lst = [Foo() for x in range(5)]while lst: delete_lst =  for i, x in enumerate(lst): x.update() if x.time == 0: delete_lst.append(i) for i in reversed(delete_lst): del lst[i]print "Done!"
To summarize what's going on, a list of 5 Foo objects gets created, each with random time values, and added to a list. After that, a loop is started that calls .update() on each Foo that decrements its .time variable by 1. Once the time value reaches zero, then the object is deleted from the list.
This is clumsy. I can't delete items while I'm iterating through the list, or I'll invalidate the iterator. So I have to keep track of the index of which items need to be deleted, and then actually delete them in a second loop. Also, I have to delete the items in reverse order, or I'll throw off the indexes and delete the wrong items.
I've written code like this several times when working with vectors in C++. So it's familiar, but writing it in Python makes me almost wince.
I felt sure that I should be able to improve this code with a list comprehension, but until this morning, I couldn't see how. But sometimes the drive to work gives me some clarity.
Here's the improved list comprehension version:import randomclass Foo(object): def __init__(self): self.time = random.randint(1, 100) def update(self): self.time -= 1 return self.time > 0 lst = [Foo() for x in range(5)]while lst: lst = [x for x in lst if x.update()] print "Done!"
Whew - that's so much nicer. The list comprehension updates all items in the list every time through the loop while automatically filtering out the ones that have a time value of zero.
There is the disadvantage of constantly generating additional lists each time through the loop, but since I don't have many objects to manage, I think the code reduction (and resulting clarity) outweighs the performance hit in this scenario.
Of course, other Python gurus probably already know about this trick, but I felt proud of myself for figuring it out.
UPDATE -- It would be nice to post this idiom to the Python Cookbook, but I'm a little baffled as to what to call it. Suggestions?
Labels: code, idiom, python