ProgramaciónDesarrollador Backend

Explique el protocolo 'Sequence' en Python, cómo implementarlo y en qué se diferencia de los objetos simplemente iterables.

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

En Python, el protocolo 'Sequence' define una interfaz para objetos que se pueden indexar e iterar, como listas o tuplas. Históricamente, las secuencias aparecieron prácticamente desde las primeras versiones de Python para respaldar las operaciones naturales con estructuras de datos: indexación, cortes, iteración sobre elementos.

Problema — para que una clase de usuario se comporte como una secuencia, no son suficientes solo los métodos iter y next. Para un soporte completo del comportamiento de la secuencia, se requieren métodos adicionales.

Solución — para implementar su propio tipo de secuencia, debe definir los métodos getitem (necesario para indexación y cortes) y opcionalmente len (para len() y verificación de longitud). Así, el objeto admitirá la iteración, acceso por índice, trabajo con cortes, así como muchas operaciones estándar de Python con secuencias.

Ejemplo de código:

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

Características clave:

  • El objeto admite acceso por índice y cortes a través de getitem.
  • Para soportar funciones len(), se necesita len.
  • La iteración se basa en getitem, no en iter, si iter no está definido.

Preguntas capciosas.

Si solo implemento iter y next, ¿se convertirá mi objeto en una secuencia (Sequence)?

No. Tal objeto será solo iterable (iterable), pero no una secuencia. No soportará indexación, cortes, ni las funciones estándar de objetos similares a listas.

¿Es necesario implementar getitem para soportar el ciclo for?

No es necesario. Si iter está implementado, for funcionará. Pero si no hay iter, el intérprete intentará usar getitem, comenzando desde el índice 0, hasta que se produzca un IndexError. Por lo tanto, para la secuencia, es suficiente con getitem.

¿Se puede implementar getitem solo para int y no para slice?

Técnicamente el objeto funcionará para c[0], pero intentar tomar un corte c[1:4] fallará. Para soportar cortes, getitem debe poder manejar objetos de tipo slice (mire slice.indices e isinstance(key, slice)).

Ejemplo de código:

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

Errores comunes y anti-patrones

  • Definen solo iter, esperando que el objeto se convierta en una secuencia completa.
  • Implementan getitem, pero no manejan objetos slice.
  • No implementan len, lo que provoca que len(obj) cause un TypeError.

Ejemplo de la vida real

Caso negativo

Implementaron una estructura personalizada, definiendo solo iter, pensando que ahora se pueden utilizar cortes e indexación.

Ventajas:

  • Funciona en un ciclo for y admite generadores.

Desventajas:

  • No hay soporte para la sintaxis obj[5] o obj[1:3], fallando con un error.
  • len(obj) tampoco funciona.

Caso positivo

La clase implementa getitem con soporte para trabajar con cortes y índices int, así como len.

Ventajas:

  • Se apoyan todas las operaciones de la secuencia: cortes, indexación, len, iteración.
  • Integración con la biblioteca estándar (por ejemplo, random.choice).

Desventajas:

  • Es necesario implementar cuidadosamente el manejo de slice, de lo contrario, pueden ocurrir errores al trabajar con cortes.