ProgramaciónDesarrollador de bibliotecas Python

¿Cómo funciona el método __getitem__ en Python, por qué implementarlo y qué se debe considerar al manejar cortes (slice)?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta

El método getitem fue añadido a Python como parte del protocolo de secuencias y mapeos. Con este método mágico, las colecciones estándar (list, tuple, dict, etc.) soportan acceso por índice, clave y corte. En las clases personalizadas, este método permite que los objetos se comporten "como colecciones".

Problema

Sin la implementación de getitem, la clase personalizada no soporta la indexación y la iteración por for. La implementación solo para índices simples no es aplicable si queremos un funcionamiento completo con cortes (slice), y la ignorancia de las variaciones de índices llevará a errores.

Solución

Implementar getitem para soportar tanto índices como objetos del tipo slice, separándolos en el procesamiento. Esto permite usar objetos personalizados en las construcciones estándar de Python (por ejemplo, para soportar cortes your_obj[1:5]).

Ejemplo de código:

class MyRange: def __init__(self, n): self.n = n def __getitem__(self, item): if isinstance(item, int): # Índice individual if 0 <= item < self.n: return item * 2 raise IndexError('índice fuera de rango') elif isinstance(item, slice): # Corte return [self[i] for i in range(*item.indices(self.n))] else: raise TypeError('Tipo de argumento no válido: {}'.format(type(item))) mr = MyRange(10) print(mr[3]) # 6 print(mr[2:5]) # [4, 6, 8]

Características clave:

  • getitem es la base del protocolo de secuencias e iterabilidad.
  • Para soportar cortes, se debe procesar slice por separado.
  • Las excepciones IndexError y TypeError son necesarias para el correcto funcionamiento de las funciones estándar (por ejemplo, list(), for).

Preguntas capciosas.

¿Proporciona la implementación solo de getitem la posibilidad de asignar valores por índice?

No. Para soportar la asignación (your_obj[i] = value) es necesario implementar setitem. getitem solo se encarga de la lectura.

¿Debería getitem necesariamente devolver una lista en el corte, como lo hace list?

No. Lo principal es devolver una "secuencia" teniendo en cuenta el significado de la clase (se puede devolver el mismo tipo o, por ejemplo, un tuple). Lo importante es que tenga sentido en el contexto de la tarea.

¿Por qué a veces aparece el error TypeError: 'MyClass' object is not subscriptable?

Este mensaje aparece si intentas ejecutar my_obj[0] y la clase no implementa getitem. Para que la clase sea subscriptable (soportara []), este método es obligatorio.

Errores típicos y anti-patrones

  • Solo verifican int, ignorando slice: los cortes siempre fallan con un error.
  • Regresan un tipo incorrecto (por ejemplo, None en cortes en lugar de una secuencia).
  • No lanzan IndexError, lo que hace que el ciclo for se comporte de manera incorrecta.

Ejemplo de la vida real

Caso negativo

Se implementó getitem solo para int, se olvidaron del slice. Cualquier intento de my_obj[2:5] lleva a TypeError y falla todo el algoritmo de procesamiento de colecciones.

Ventajas:

  • Sencillez.

Desventajas:

  • No se soportan cortes, el código no es compatible con la mayoría de las construcciones estándar.

Caso positivo

getitem implementado con un procesamiento separado para slice e int. Los cortes e índices funcionan, los métodos list(), map(), y la iteración se soportan sin esfuerzos adicionales.

Ventajas:

  • La clase es compatible con las herramientas estándar de Python.
  • Fácil de extender para cualquier tipo de índices (tuple, str para matrices, etc.).

Desventajas:

  • La implementación requiere un poco más de código y prueba.