python замораживает список перед циклом for? - PullRequest
5 голосов
/ 01 марта 2012

Скажем, у меня есть список l и поток t1 , повторяющийся по l навсегда:

while True:
    for i in l:
        #do something

и другой поток t2 произвольно изменяемыйили удалить участников в l.

Что происходит после удаления? t1 обнаруживает это в токовой петле?

ОБНОВЛЕНИЕ :

  1. Под заморозкой я имею в виду t1 получить копию l. t2 может изменить l наверняка

  2. со ссылкой на документацию или простой, но убедительный фрагмент кода приветствуются.

Ответы [ 3 ]

7 голосов
/ 01 марта 2012

Нет. Список не заморожен - ваш итератор не «сломается» в смысле вызова исключения. Вместо этого он будет продолжать двигаться вперед по списку с результатами, которые могут быть неожиданными.

Рассмотрим этот код (фрагмент кода на ideone здесь: 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

Он производит такой вывод:

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

Что, вероятно, не то, что вы хотели или ожидали, хотя, по-видимому, оно четко определено: http://docs.python.org/reference/compound_stmts.html#the-for-statement.

Вместо этого вы можете заблокировать список или сделать копию. Обратите внимание, что iter(l) не берет внутреннюю копию и будет иметь тот же эффект, что и простая итерация по списку напрямую.

2 голосов
/ 01 марта 2012

Вот что вы можете наблюдать:

>>> 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"

Отсюда видно, что изменение списка в одном потоке повлияло на итератор в другом потоке; удаление первого элемента заставило итератор пропустить элемент, удаление будущего элемента означает, что его не будет видно в итераторе, потому что он пропал.

Вот модель того, как это работает; Я не предоставляю код для этого явно, но вы можете решить это из наблюдения.

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

Перейти к следующему пункту.

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

Удалить первый элемент.

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

Перейти к следующему пункту.

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

Удалить пятый элемент.

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

и так далее. Что касается потоков, то не имеет значения, делаете ли вы это в одном или нескольких потоках. Конечно, у вас может быть условие гонки, независимо от того, удален элемент или нет, но он все равно работает аналогичным образом.

Если вы не знакомы с внутренностями итерации, встречайте iter. for x in y итерирует по iter(y), на самом деле. Таким образом, вы можете поиграть с объектом iter(liszt) listiterator, если хотите, используя next(), пока вы играете со списком, над которым он итерирует. Удобнее, чем цикл for в интерактивной консоли Python.

0 голосов
/ 01 марта 2012

список доступен из обеих тем и не заморожен. итератор «узнает», что члены были удалены, и пропустит их, если у вас нет явного доступа к индексу за пределами списка. сами удаления являются потокобезопасными.

если вы хотите реальную блокировку списка, защитите его мьютексом или скопируйте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...