Domanda

Alright, I am super new to Python and something is bugging me about slicing lists. Why is it that I get [1, 3, 4] back when I am slicing [1] and [3] from this code?

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

I assumed i would be getting [1, 3, 5] back since it appears [2] and [4] are being removed.

if-->[1, 2, 3, 4, 5]
is-->[0, 1, 2, 3, 4]

Where is it that my logic is getting messed up?

È stato utile?

Soluzione

The first deletion changes the list indices, so the next one isn't where it was before... Simplified

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

Altri suggerimenti

The deletions are done one at a time. The first deletion shifts all the subsequent elements one position to the left:

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

If you arrange the indices from the largest to the smallest, this doesn't happen since no deletion changes any relevant indices:

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

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

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

When you are doing your deletions, they happen one at a time, so every deletion causes the rightward values to move over one index to the left.

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]

What you should do to preserve the order is order your deletions from largest to smallest index so nothing is shifted over, i.e.,

del z[3]; del z[1]

Even better is to make all your deletions at once to avoid the problem entirely:

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

The reason you see this behavior is that del z[1], z[3] is exactly equivalent to having del z[1] and del z[3] on separate lines. So the first del statement will shift subsequent elements and z[3] becomes the last element (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

Other's have mentioned what is wrong with your code, and I can truly understand the reasoning for what you intend to do

In case you want to delete the 2nd and 4th element and care less about the shifting list, you should do it in a single bredth, a possible solution would be through List COmprehension

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]

A better solution would be to use 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]
>>> 
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top