Запутывание списка нарезки в 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]
>>>