Show Python list slicing with a step, negative indices, and the fact that a slice creates a new list.
List slicing works exactly like string slicing: text[start:stop:step], with the stop index excluded and the step controlling the direction and spacing.
items = [0, 1, 2, 3, 4, 5]
print(items[1:4]) # [1, 2, 3]
print(items[:3]) # [0, 1, 2]
print(items[::2]) # [0, 2, 4]
print(items[::-1]) # [5, 4, 3, 2, 1, 0]
print(items[-2:]) # [4, 5]
# A slice always produces a NEW list
copy = items[:]
copy.append(99)
print(items) # [0, 1, 2, 3, 4, 5] - unchanged
print(copy is items) # FalseThe reversal by items[::-1] and the last-two-elements slice by items[-2:] both work because a negative step starts from the end and negative indices count backwards from the end.
The copy semantics at the bottom are the important part for interviews. A slice always builds a new list, so items[:] is an independent shallow copy. Mutating it does not affect the original, and the two are different objects.
But it is a shallow copy. If the list contains mutable objects, those inner objects are still shared between the original and the copy. For a list of lists, items[:] gives you new outer list but the same inner lists. If you need a genuinely independent structure, use copy.deepcopy.
One more thing worth knowing is that you can also assign to a slice, and the lengths do not have to match. Assigning a shorter list to a slice replaces those positions and removes the extras, while assigning a longer one inserts the extra items. That behaviour is occasionally useful, but it is also a classic source of accidental data loss, so slice assignment deserves the same care as slice indexing.