Запутывание списка нарезки в Python

Хорошо, я супер новичок в Python, и что-то беспокоит меня по поводу нарезки списков. Почему я получаю [1, 3, 4] обратно, когда нарезаю [1] и [3] из этого кода?

z = [1, 2, 3, 4, 5]
del z[1], z[3]
print z

Я предполагал, что получу [1, 3, 5] обратно, так как кажется, что [2] и [4] удаляются.

если -> [1, 2, 3, 4, 5]
is ->[0, 1, 2, 3, 4]

Где моя логика запуталась?

5 ответов

Решение

Первое удаление изменяет индексы списка, поэтому следующее не там, где это было раньше... Упрощено

>>> a = [1, 2, 3]
>>> del a[0] # should delete 1
>>> a
[2, 3]
>>> del a[1] # This use to be the index for 2, but now `3` is at this index
>>> a
[2]

Удаление выполняется по одному. Первое удаление сдвигает все последующие элементы на одну позицию влево:

In [3]: z = [1, 2, 3, 4, 5]

In [5]: del z[1]

In [6]: z
Out[6]: [1, 3, 4, 5]

In [7]: z[3]
Out[7]: 5

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

In [18]: z = [1, 2, 3, 4, 5]

In [19]: del z[3], z[1]

In [20]: z
Out[20]: [1, 3, 5]

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

z = [1, 2, 3, 4, 5]

del z[1] # [1, X, 3, 4, 5] --> [1, 3, 4, 5]

del z[3] # [1, 3, 4, X] --> [1, 3, 4]

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

del z[3]; del z[1]

Еще лучше сделать все ваши удаления сразу, чтобы полностью избежать этой проблемы:

z = [1, 2, 3, 4, 5]
del z[1::2]
# returns [1, 3, 5]

Причина, по которой вы видите это поведение в том, что del z[1], z[3] в точности эквивалентно del z[1] а также del z[3] на отдельных строках. Итак, первый del заявление сместит последующие элементы и z[3] становится последним элементом (5):

>>> def del_test():
...     del z[1], z[3]
...
>>> def del_test2():
...     del z[1]
...     del z[3]
...
>>> del_test.__code__.co_code == del_test2.__code__.co_code
True

Другие упоминали, что не так с вашим кодом, и я действительно могу понять причину того, что вы намерены делать

В случае, если вы хотите удалить 2-й и 4-й элемент и не заботиться о смещающемся списке, вы должны сделать это за один раз, возможное решение будет через List COmppresion

def inplace_del(it, rng): 
    return [e for i, e in enumerate(it) if i not in set(rng)]
inplace_del(z,(2,3))
[1, 2, 5]
y = xrange(1,6)
inplace_del(y,(2,3))
[1, 2, 5]

Лучшим решением было бы использовать itertools

>>> from itertools import count, compress
>>> def in_del(it, rng):
    rng = set(rng)
    holes = (e not in rng for e in count(0))
    return compress(it, holes)

>>> list(in_del(z,(2,3)))
[1, 2, 5]
>>> list(in_del(y,(2,3)))
[1, 2, 5]
>>> 
Другие вопросы по тегам