2

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?

tdy
  • 36,675
  • 19
  • 86
  • 83
Mohammad Rahimi
  • 965
  • 5
  • 15
  • 1
    For contiguous slices, the semantic of assigning an iterable of a differing length is relatively clear. For non-contiguous slices, less so: imagine `numbers[::2] = [1]`, what is that supposed to mean? – user2390182 Jul 26 '21 at 08:53
  • i guess i'm not seeing the ambiguity. wouldn't it just be `[1, 1, 1, 3, 1, 5, 1, 7, 1, 9]`? what would the alternative interpretation(s)? – tdy Jul 26 '21 at 09:01
  • `numbers[0:2]` is a `slice`, while `numbers[::2]` is an `extended slice`. if the question is really about what prevents this and not why it prevents this, then this is the answer – Almog-at-Nailo Jul 26 '21 at 09:03
  • @tdy what then is the interpretation of `numbers[::2] = [1, 2]`? – Almog-at-Nailo Jul 26 '21 at 09:05
  • @ExtraFishness `[1, 2, 1, 1, 2, 3, 1, 2, 5, 1, 2, 7, 1, 2, 9]` – tdy Jul 26 '21 at 09:07
  • why not `[1, [1, 2], 2, [1,2], 3, [1,2], 4, [1,2], 5...]`? it's ambigous... – Almog-at-Nailo Jul 26 '21 at 09:08
  • then why is `numbers[3:5] = [1, 2]` not `[0, 1, 2, [1, 2], 5, 6, 7, 8, 9]`? so to be more precise, i don't see a difference in ambiguity between slice/extended slice. – tdy Jul 26 '21 at 09:10
  • @ExtraFishness so some where in python internals there is a difference betwwen `slices` ans `extended slice`? Can you guide me to documentation for extended slice? or is there a comparison between two somewhere? Thanks. – Mohammad Rahimi Jul 26 '21 at 09:26
  • 1
    @tdy Say the list is `numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]` and then we perform `numbers[::2] = [1, 2]`. The reason it is ambiguous is because there are different ways the assignment could happen, whether `[1, 0, 2, 0, 1, 0, 2, 0, 1, 0]` or `[1, 0, 2, 0, None, 0, None, 0, None, 0]` or `[1, 0, 2, 0, 2, 0, 2, 0, 2, 0]` or `[1, 0, 1, 0, 1, 0, 1, 0, 2, 0]` or `[[1, 2], 0, [1, 2], 0, [1, 2], 0, [1, 2], 0, [1, 2], 0]`. Unlike performing `numbers[3:9] = [1, 2]` which has only 1 meaning, which is to remove items 3:9 then put [1,2] in its place producing `[0, 0, 0, 1, 2, 0]`. – Niel Godfrey Pablo Ponciano Jul 26 '21 at 09:58
  • I'm an experienced C++ developer. Why I have such hard time to understand how Python interpreter thinks? – Mohammad Rahimi Jul 26 '21 at 10:15
  • @NielGodfreyPonciano Any of those interpretations could just as easily apply to `numbers[3:9] = [1,2]` though: `[0,0,0,1,2,1,2,1,2,0]`, `[0,0,0,1,2,None,None,None,None,0]`, `[0,0,0,[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],0]`, etc. To me the only explanation that's made sense is what @kaya3 said about how the RHS is only supposed to get used *once* on the LHS during slice assignment. – tdy Jul 26 '21 at 10:20

3 Answers3

5

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

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.

kaya3
  • 47,440
  • 4
  • 68
  • 97
  • i'm still not seeing how the proposed extended slice assignment differs from the current slice assignment behavior. e.g., `numbers[3:5] = [0,0,0,0]` currently gives `[0,1,2,0,0,0,0,5,6,7,8,9]`, so to me it seems consistent that `numbers[::2] = [0,0,0,0]` would give `[0,0,0,0,1,0,0,0,0,3,0,0,0,0,5,0,0,0,0,7,0,0,0,0,9]`. – tdy Jul 26 '21 at 09:27
  • 1
    @tdy In `numbers[3:5] = [a,b,c,d]` each of `a`, `b`, `c` and `d` occurs once in the right-hand list and will occur once in the left-hand list after the assignment. Normal slice assignment cannot duplicate values. – kaya3 Jul 26 '21 at 09:28
  • ah ok, i see now – tdy Jul 26 '21 at 09:29
  • @tdy Also consider the case `numbers[::2] = [5, 6, 7, 8]` which would do rather different things for lists of length 7 and 8 compared to lists of other lengths. – kaya3 Jul 26 '21 at 09:30
1

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.

gabriele
  • 39
  • 3