Безопасно ли (документированное поведение?) Удалить домен итератора во время выполнения? - PullRequest
4 голосов
/ 03 июня 2010

Я хотел бы знать, безопасно ли (задокументированное поведение?) Удалять доменное пространство итератора при выполнении в Python.

Рассмотрим код:

import os
import sys

sampleSpace = [ x*x for x in range( 7 ) ]

print sampleSpace

for dx in sampleSpace:

    print str( dx )

    if dx == 1:

        del sampleSpace[ 1 ]
        del sampleSpace[ 3 ]

    elif dx == 25:

        del sampleSpace[ -1 ]

print sampleSpace

'sampleSpace' - это то, что я называю 'доменным пространством итератора' (если есть более подходящее слово / фраза, позвольте мне знать).

Что я делаю, это удаляю значения из него, пока итератор 'dx' проходит через него.

Вот что я ожидаю от кода:

Iteration versus element being pointed to (*):

0: [*0, 1, 4, 9, 16, 25, 36]
1: [0, *1, 4, 9, 16, 25, 36] ( delete 2nd and 5th element after this iteration )
2: [0, 4, *9, 25, 36]
3: [0, 4, 9, *25, 36] ( delete -1th element after this iteration )
4: [0, 4, 9, 25*] ( as the iterator points to nothing/end of list, the loop terminates )

.. и вот что я получаю:

[0, 1, 4, 9, 16, 25, 36]
0
1
9
25
[0, 4, 9, 25]

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

Следовательно - я хотел спросить вас, есть ли в Python какое-то правило типа «итератор становится недействительным, если вы изменяете его пространство во время итерации»?

Безопасно ли (задокументированное поведение) в Python делать подобные вещи?

Ответы [ 3 ]

6 голосов
/ 03 июня 2010

С учебник по Python :

Не безопасно изменять последовательность повторяется в цикле (это может произойти только для изменяемой последовательности типы, такие как списки). Если вам нужно изменить список, который вы перебираете (например, для дублирования выбранного элементы) вы должны перебрать копию. Запись среза делает это особенно удобно:

>>> for x in a[:]: # make a slice copy of the entire list
...    if len(x) > 6: a.insert(0, x)
...
>>> a
['defenestrate', 'cat', 'window', 'defenestrate']
1 голос
/ 03 июня 2010

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

То, что происходит в вашем примере,

# list is [0, 1, 4, 9, 16, 25, 36]

if dx == 1:
    # we're at index 1 when this is true
    del sampleSpace[ 1 ]
    # we've removed the item at index 1, and the iterator will move to the next valid position - still index 1, but in a mutated list. We got lucky in this case
    # the list now contains [0, 4, 9, 16, 25, 36]
    del sampleSpace[ 3 ]   
    # we remove the item at index 3 which is (now) value 16
    # the list now contains [0, 4, 9, 25, 36]
elif dx == 25:

    del sampleSpace[ -1 ]
    # we remove the final item, list now looks like
    # the list now contains [0, 4, 9, 25]
0 голосов
/ 03 июня 2010

Что вы подразумеваете под сейфом? Ваш код не вызывает каких-либо ошибок, но это, конечно, вполне вероятно, учтите это:

>>> a = range(3)
>>> for i in a:
    del a


Traceback (most recent call last):
  File "<pyshell#13>", line 2, in <module>
    del a
NameError: name 'a' is not defined
>>> a
[0, 1, 2]
>>> for i in a:
    del a[i+1]


Traceback (most recent call last):
  File "<pyshell#27>", line 2, in <module>
    del a[i+1]
IndexError: list assignment index out of range

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

...