ProgramaciónDesarrollador Backend

¿Cómo se implementa la interfaz de secuencia (Sequence) en Python? ¿Para qué es necesario implementar los métodos especiales __getitem__, __len__, y qué trampas existen?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta:

Las secuencias son uno de los conceptos más antiguos y básicos en Python. Ejemplos clásicos son listas (list), cadenas (str), y tuplas (tuple). Se ha definido un protocolo especial para interactuar con secuencias: es necesario implementar los métodos __getitem__ y __len__. Esto permite que un objeto se comporte "como una secuencia" — soportar indexación, cortes, trabajo en bucles y hasta algunas funciones estándar.

Problema:

Sin la implementación correcta de estos métodos, una clase de usuario no podrá trabajar con operaciones de indexación, bucles for, funciones como len(). A menudo, los desarrolladores principiantes implementan solo uno de los métodos, no consideran el manejo de excepciones, no implementan el soporte para cortes (slice) y obtienen un comportamiento incorrecto o inesperado.

Solución:

Es necesario implementar el método __getitem__(self, key) para soportar la indexación y los cortes, así como __len__(self) para trabajar con la función len() y una correcta iterabilidad. Para soportar cortes, es necesario diferenciar el tipo de key en __getitem__ y manejar correctamente los objetos slice.

Ejemplo de código:

def is_even(n): return n % 2 == 0 class EvenSequence: def __init__(self, size): self.size = size def __getitem__(self, index): if isinstance(index, slice): return [x for x in range(self.size)[index] if is_even(x)] if index < 0 or index >= self.size: raise IndexError('Índice fuera de límites') return index if is_even(index) else None def __len__(self): return self.size

Características clave:

  • Interacción con la mayoría de funciones y sintaxis de Python, si se implementan ambos métodos.
  • Para soportar cortes, se deben manejar objetos del tipo slice en getitem.
  • Al no implementar len, el objeto no funcionará con len() y algunas funciones estándar se comportarán de manera limitada.

Preguntas capciosas.

¿Se puede limitar solo a implementar el método getitem para que el objeto funcione como una secuencia?

Parcialmente. Si se implementa solo __getitem__, se podrá iterar sobre el objeto mediante for e indexar elementos, pero len() no funcionará.

Ejemplo de código:

class SeqOnlyGetitem: def __getitem__(self, index): if index >= 10: raise IndexError return index * 2 s = SeqOnlyGetitem() for x in s: print(x) # Funciona (iterable) # print(len(s)) # Error TypeError

¿Cómo manejar índices negativos y qué pasará si no se consideran?

Los índices negativos son una característica clave de las secuencias en Python. Si no se manejan, el objeto se comporta de manera inesperada para el usuario.

class NegIndex: def __init__(self, data): self.data = data def __getitem__(self, index): if index < 0: index += len(self.data) return self.data[index]

¿Es necesario implementar contains o iter para su clase de Sequence?

No es obligatorio. Si se implementan __getitem__ y __len__, la función in funcionará, y for las utilizará para iterar. La implementación de estos métodos directamente generalmente no es necesaria, pero puede mejorar el rendimiento.

Errores típicos y anti-patrones

  • No verifican el rango de índices, lo que lleva a IndexError o resultados inesperados.
  • No implementan el soporte para cortes (slice), por lo que obj[2:10:2] provoca un error.
  • Olvidan los índices negativos (obj[-1]).

Ejemplo de la vida real

Caso negativo

Un desarrollador implementó solo getitem para su clase solo para índices positivos. El módulo pasó parte de las pruebas unitarias, pero en el uso real, al intentar acceder a un índice inexistente o un índice negativo, todo falló.

Ventajas:

  • Implementación inicial rápida.

Desventajas:

  • Errores inesperados en la práctica.
  • Inconvenientes en su uso (no se pueden tomar los últimos elementos a través de un índice negativo, no funciona el corte, no funciona len()).

Caso positivo

El equipo implementó ambos métodos (getitem y len), consideraron los cortes, índices negativos, y lanzaron excepciones correctas. La clase resultante funcionó en todos los casos estándar de Python.

Ventajas:

  • Comportamiento predecible desde el punto de vista de la API de Python.
  • Facilidad de uso, mínimo de errores.

Desventajas:

  • Un poco más de código, atención al detalle requerida en el diseño.