Почему странное поведение понимания списка с побочными эффектами?

Я знаю, что использование побочных эффектов в понимании списков Python не является хорошей практикой. Но я не могу понять, почему происходит что-то вроде следующего:

In [66]: tmp = [1,2,3,4,5]; [tmp.remove(elem) for elem in tmp]
Out[66]: [None, None, None]

In [67]: tmp
Out[67]: [2, 4]

Независимо от того, является ли это хорошей практикой, не должна ли внутренняя часть понимания списка делать что-то предсказуемое? Если приведенное выше является предсказуемым, может ли кто-нибудь объяснить, почему произошло только три операции remove и почему остались только четные записи?


person ely    schedule 16.10.2012    source источник
comment
stackoverflow.com/questions/2442651/   -  person Josh Lee    schedule 17.10.2012
comment
@JoshLee Мне не нужно удалять элементы как таковые, я просто хотел понять, почему None or... не работает. Оказывается, это работало, и я не понимал, что индекс меняется.   -  person ely    schedule 17.10.2012
comment
Кроме того, в примере, из которого это происходит, часть, где я помещаю None, на самом деле является дорогостоящим вычислением, которое может привести к возврату None, и в этом случае удаление элементов из tmp, где это происходит, может быть жизнеспособным выбор за мной. Но в любом случае было бы нехорошо также включать предложение if в конце понимания списка, потому что это потребовало бы повторного вычисления этой дорогостоящей функции, чтобы знать, что оставить.   -  person ely    schedule 17.10.2012
comment
Ну, вы всегда можете переписать None or x просто как x.   -  person Josh Lee    schedule 17.10.2012
comment
None может быть возвращено дорогой функцией в моем реальном коде.   -  person ely    schedule 17.10.2012
comment
Конечно. Но чтобы довести этот пример до его сути, было бы полезно понять, что каждый None в вашем результате на самом деле является возвращаемым значением из tmp.remove.   -  person Josh Lee    schedule 17.10.2012
comment
Неплохо подмечено. Я отредактирую. Но я думаю, что это все же отличается от связанного вопроса, в котором простое условие в понимании решает проблему.   -  person ely    schedule 17.10.2012


Ответы (2)


Речь идет не о списках, а об удалении из списков, которые вы повторяете:

>>> tmp = [1,2,3,4,5]
>>> for elem in tmp:
...     tmp.remove(elem)
... 
>>> tmp
[2, 4]

Это выглядит примерно так:

>>> tmp = [1,2,3,4,5]
>>> for elem in tmp:
...     print elem, tmp
...     tmp.remove(elem)
...     print elem, tmp
... 
1 [1, 2, 3, 4, 5]
1 [2, 3, 4, 5]
3 [2, 3, 4, 5]
3 [2, 4, 5]
5 [2, 4, 5]
5 [2, 4]

Сначала он смотрит на 0-й элемент, а 1 удаляется. Итак, на следующей итерации он хочет удалить 1-й элемент, который теперь является 3-м, и т. д.

person DSM    schedule 16.10.2012
comment
Попался, поэтому, если я сделаю это [None or tmp.remove(elem) for elem in list(tmp)] или [None or tmp.remove(elem) for elem in tmp[:]], тогда он должен работать, как и ожидалось (все еще плохой стиль и все такое). - person ely; 17.10.2012

Никогда не рекомендуется удалять из списка, поскольку вы перебираете его напрямую. Редактировать: упс, я перепутал списки со словарями. У словарей есть методы iteritems() и iterkeys(), которые вы бы использовали, когда перебираете словарь и удаляете из него элементы.

Для списка вы, вероятно, захотите скопировать список, если хотите сделать это внутри списка. Или поочередно:

[None and tmp.pop(0) for i in xrange(len(tmp))]
person Santiclause    schedule 16.10.2012