ProgrammingBackend Developer

Explain the 'Sequence' protocol in Python, how to implement it, and how it differs from just iterable objects?

Pass interviews with Hintsage AI assistant

Answer.

In Python, the 'Sequence' protocol defines an interface for objects that can be indexed and iterated over, such as lists or tuples. Historically, sequences appeared almost from the earliest versions of Python to support natural operations on data structures: indexing, slicing, and iterating over elements.

Problem — In order for a user-defined class to behave like a sequence, simply having the methods iter and next is not enough. To fully support sequence behavior, additional methods are required.

Solution — To implement your own sequence type, you need to define the getitem method (necessary for indexing and slicing) and optionally len (for len() and checking the length). This way, the object will support iteration, indexing, slicing, as well as many standard Python operations with sequences.

Example code:

class MyCounter: def __init__(self, stop): self._stop = stop def __getitem__(self, index): if 0 <= index < self._stop: return index * 10 else: raise IndexError('Out of range') def __len__(self): return self._stop c = MyCounter(5) print(c[3]) # 30 print(len(c)) # 5 for x in c: print(x)

Key features:

  • The object supports indexing and slicing via getitem.
  • len is needed to support len() function.
  • Iteration is built on getitem rather than iter if iter is not defined.

Tricky questions.

If I implement only iter and next, will my object be a Sequence?

No. Such an object will only be iterable, but not a sequence. It will not support indexing, slicing, or standard list-like object functions.

Is it necessary to implement getitem to support the for loop?

Not necessarily. If iter is implemented, for will work. But if there is no iter, the interpreter will try to use getitem, starting from index 0, until an IndexError is raised. Therefore, for a sequence, getitem is sufficient.

Can getitem be implemented only for int, but not for slice?

Technically, the object will work for c[0], but trying to take a slice c[1:4] will break. To support slicing, getitem must be able to handle objects of type slice (see slice.indices and isinstance(key, slice)).

Example code:

class S: def __getitem__(self, idx): if isinstance(idx, slice): return [x for x in range(idx.start or 0, idx.stop or 10, idx.step or 1)] return idx * 2

Common mistakes and anti-patterns

  • Only defining iter, expecting the object to become a full-fledged sequence.
  • Implementing getitem but not handling slice objects.
  • Not implementing len, causing len(obj) to raise TypeError.

Real-life example

Negative case

Implemented a custom structure, defining only iter, thinking that slicing and indexing could now be used.

Pros:

  • Works in for loops and supports generators.

Cons:

  • No support for obj[5] or obj[1:3] syntax, raises an error.
  • len(obj) also does not work.

Positive case

Class implements getitem with support for both slicing and int indexing, as well as len.

Pros:

  • Supports all sequence operations: slicing, indexing, len, iteration.
  • Integration with the standard library (e.g., random.choice).

Cons:

  • Need to carefully implement slice handling; otherwise, bugs may arise while working with slices.