Why doesn't slicing an empty list throw an error?

I have the same question, if there is only one element in the list why doesn’t lst[1:] throw an IndexError?

# Codecademy's supplied solution

def delete_starting_evens(lst):
  while len(lst) and lst[0] % 2 == 0:
    lst = lst[1:]
    
  return lst

print(delete_starting_evens([4, 8, 10, 11, 12, 15])) # [11, 12, 15]
print(delete_starting_evens([4, 8, 10])) # []

With respect to the second invocation, this is what the list looks like after each iteration of the while loop:

[4, 8, 10] # Original list

[8, 10] # After first iteration
[10] # After second iteration
[] # After third iteration

Why does attempting to reference lst[1] not fail after the second iteration?

1 Like

lst[1] would give an index error, lst[1:] doesn’t. Python does some clever magic to return an empty list.

2 Likes

I see, I didn’t expect to find magic in Python… the lesson on list slicing should really mention this.

1 Like

i am not sure how under the hood python is managing this, so i designated it magic. But it seems list slicing can handle highest index + 1 to give back an empty list.

maybe @mtf knows?

1 Like

I don’t think it is magic, or even secret sauce. A slice is a copy, not the actual list. As such it is an empty list until populated.

3 Likes

Because starting at index 1 and iterating to the end results in zero iterations, nothing weird about it

However, you’ll still only get as much as there is if you explicitly specify a range that isn’t there, so the bounds are rather soft/forgiving and I’m not sure if I like that or not, not sure if there are cases where this helps, maybe there is. It hasn’t bothered me or given me any trouble though so I have no complaints. You can think of it as specifying whatever is there, if anything.

One case where you indeed may want to specify locations that don’t exist is when you’re assigning to those locations, maybe symmetry with that is one reason for the soft bounds.

1 Like