Mohammad Rahimi
Mohammad Rahimi

Reputation: 1123

Delete every other element with slicing

Consider this:

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

These are correct statements in Python to remove elements:

numbers[0:2] = []
numbers[3:5] = []

However the statement below is not allowed:

numbers[::2] = []

ValueError: attempt to assign sequence of size 0 to extended slice of size 5

What prevents such a statement in Python?

Upvotes: 2

Views: 588

Answers (3)

gabriele
gabriele

Reputation: 39

You have to create a new list with the slicing already mentioned in other answers:

numbers = numbers[1::2]

if you work on the same list you incur into heavy performance loss because inserting or deleting (not appending!) an element to a list is O(N). Since you have O(N) for each insertion and you have N/2 insertions your total cost is O(N**2). You really don't want that cost. Creating a new list with the output of the slicing, on the other hand, has just O(N) total cost.

Upvotes: 1

Niel Godfrey P. Ponciano
Niel Godfrey P. Ponciano

Reputation: 10719

ValueError: attempt to assign sequence of size 0 to extended slice of size 5

What prevents such a statement in Python?

It is noted in the documentation that the replacement must have the same length for the case where there is an explicit step (which is 2 in your case).

Operation Result Notes
s[i:j] = t slice of s from i to j is replaced by the contents of the iterable t
s[i:j:k] = t the elements of s[i:j:k] are replaced by those of t (1) t must have the same length as the slice it is replacing.

The correct way is also documented there.

Operation Result Notes
del s[i:j] same as s[i:j] = []
del s[i:j:k] removes the elements of s[i:j:k] from the list

Code:

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
del numbers[::2]
print(numbers)

Output:

[1, 3, 5, 7, 9]

Upvotes: 5

kaya3
kaya3

Reputation: 51162

There is no need for numbers[::2] = [] to have the behaviour of deleting every second element, because you can already do that by writing numbers[:] = numbers[1::2]. If instead you want to (e.g.) replace every second element with the value 1, you can write one of the below, which are explicit about their behaviour.

for i in range(0, len(numbers), 2):
    numbers[i] = 1

# or:
numbers[::2] = [1] * len(numbers[::2])

It is not obvious what the correct behaviour should be for assigning m elements to a non-contiguous slice of n list locations when m != n. In the comments you propose a possible behaviour, but your proposal is not consistent with how slice assignment works in other cases (normally, each element on the right-hand side gets used once on the left-hand side) and certainly doesn't fulfil the principle of least astonishment. In cases like this, I think there is no (non-raising) behaviour that most people would expect, so raising an error is the best option.

Upvotes: 3

Related Questions