Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

does python freeze the list before for-loop?

Say I have a list l and a thread t1 iterating over l for ever:

while True:
    for i in l:
        #do something

and another thread t2 randomly modify or delete members in l.

What happens after a deletion? Does t1 detect that in the current loop?

UPDATE:

  1. By freeze I mean t1 get a copy of l. t2 can modify l for sure

  2. citing documentation or simple but persuading code snippet are welcomed.

like image 422
onemach Avatar asked Mar 01 '12 11:03

onemach


2 Answers

No. The list is not frozen - your iterator will not "break" in the sense of raising an exception. Instead, it will keep moving forward through the list, with results that are possibly surprising.

Consider this code (Snippet at ideone here: http://ideone.com/0iMao):

l = list(range(10))
for i in l:
    print i
    try:
        del l[i]
    except (RuntimeError,IndexError), e:
        print e

print l

It produces this output:

0
2
4
5
7
list assignment index out of range
9
list assignment index out of range
[1, 2, 4, 5, 7, 9]

Which is probably not what you wanted or expected, although it is apparently well-defined: http://docs.python.org/reference/compound_stmts.html#the-for-statement .

Instead, you can either lock the list, or take a copy. Note that iter(l) does not take a copy internally, and will have the same effect as just iterating over the list directly.

like image 98
Marcin Avatar answered Nov 10 '22 08:11

Marcin


Here is the sort of thing you can observe:

>>> from threading import Thread
>>> from time import sleep
>>> liszt = ['first item', 'second item', 'third item', 'fourth item',
...         'plentee more items', "but I'm lazy"]
>>> def thread_one():
...     for i in liszt:
...             print 'Thread one found "%s"' % i
...             sleep(1)
... 
>>> def thread_two():
...     sleep(0.5)
...     print 'Thread two deleting first item.'
...     del liszt[0]
...     sleep(1)
...     print 'Thread two deleting fourth item.'
...     del liszt[3]
... 
>>> Thread(target=thread_one).start(); Thread(target=thread_two).start()
Thread one found "first item"
Thread two deleting first item.
Thread one found "third item"
Thread two deleting fourth item.
Thread one found "fourth item"
Thread one found "but I'm lazy"

From this you can see that modifying the list in one thread affected the iterator in the other thread; deleting the first item made the iterator skip an item, deleting a future item means it won't be seen in the iterator, because it's gone.

Here is a model of some of how this works; I'm not providing code for this explicitly, but you can work this out from observation.

State:             [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Iterator position:  ^

Go to next item.

State:             [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Iterator position:     ^

Delete first item.

State:             [2, 3, 4, 5, 6, 7, 8, 9, 10]
Iterator position:     ^

Go to next item.

State:             [2, 3, 4, 5, 6, 7, 8, 9, 10]
Iterator position:        ^

Delete fifth item.

State:             [2, 3, 4, 5, 7, 8, 9, 10]
Iterator position:        ^

Et cetera. On the matter of threading, it doesn't matter whether you are doing it in one thread or multiple threads. Sure, you may have a race condition on whether the item is deleted or not, but it still works in the same way.

If you're not familiar with the internals of iteration, meet iter. for x in y is iterating over iter(y), in actual fact. So, you can play with the iter(liszt) listiterator object if you want, using next() on it while you fiddle with the list it's iterating over. More convenient than a for-loop in the interactive Python console.

like image 21
Chris Morgan Avatar answered Nov 10 '22 08:11

Chris Morgan