Métodos mágicos en clases de Python

Métodos especiales en clases de Python

Los métodos que comienzan con dos guiones bajos, como __init__, __str__, __doc__, __new__, se denominan "métodos mágicos" en Python. Estos métodos se ejecutan automáticamente ante ciertos eventos de la clase o del objeto. Para personalizar clases con funcionalidades específicas, es necesario sobrescribir estos métodos.

Python reserva todos los métodos de clase que comienzan con __ (dos guiones bajos) como métodos mágicos. Por lo tanto, al definir métodos de clase, se recomienda no usar __ como prefijo, excepto para los métodos mágicos predefinidos.

Clasificación de métodos mágicos

Métodos fundamentales

__new__(cls[, ...]): Primer método invocado al instanciar un objeto. Su primer parámetro es la clase misma, y decide si se llamará a __init__. Se usa para heredar tipos inmutables como tuplas o cadenas.

__init__(self[, ...]): Inicializador que se ejecuta al crear una instancia.

__del__(self): Destructor que se invoca al destruir una instancia.

__call__(self[, args...]): Permite que una instancia se comporte como una función: x(a, b) invoca x.__call__(a, b).

__len__(self): Define el comportamiento al usar len().

__repr__(self): Define el comportamiento al usar repr() o al inspeccionar el objeto.

__str__(self): Define el comportamiento al usar str() o al imprimir el objeto.

__bool__(self): Define el comportamiento al usar bool(), debe retornar True o False.

Métodos de atributos

__getattr__(self, name): Invocado al acceder a un atributo inexistente.

__getattribute__(self, name): Invocado al acceder a cualquier atributo de la clase.

__setattr__(self, name, value): Invocado al asignar un atributo.

__delattr__(self, name): Invocado al eliminar un atributo.

__get__(self, instance, owner): Define el comportamiento al obtener el valor de un descriptor.

__set__(self, instance, value): Define el comportamiento al modificar el valor de un descriptor.

__delete__(self, instance): Define el comportamiento al eliminar un descriptor.

Operadores de comparación

__lt__(self, other): Define el comportamiento del operador <.

__eq__(self, other): Define el comportamiento del operador ==.

__gt__(self, other): Define el comportamiento del operador >.

Operadores aritméticos

__add__(self, other): Define el comportamiento de la suma +.

__sub__(self, other): Define el comportamiento de la resta -.

__mul__(self, other): Define el comportamiento de la multiplicación *.

__truediv__(self, other): Define el comportamiento de la división real /.

__pow__(self, other[, modulo]): Define el comportamiento de la potencia **.

Operaciones inversas

__radd__(self, other): Invocado cuando el operando izquierdo no soporta la operación correspondiente.

Asignación aumentada

__iadd__(self, other): Define el comportamiento de +=.

__isub__(self, other): Define el comportamiento de -=.

Operadores unarios

__pos__(self): Define el comportamiento del operador unario +.

__neg__(self): Define el comportamiento del operader unario -.

__abs__(self): Define el comportamiento al usar abs().

Conversión de tipos

__int__(self): Define el comportamiento al usar int().

__float__(self): Define el comportamiento al usar float().

__index__(self): Convierte a entero para usar en operaciones de corte.

Contexto con sentencia with

__enter__(self): Define la inicialización al usar with.

__exit__(self, exc_type, exc_value, traceback): Define la finalización del bloque with.

Tipos de contanedor

__getitem__(self, key): Define la obtención de elementos con self[key].

__setitem__(self, key, value): Define la asignación con self[key] = value.

__delitem__(self, key): Define la eliminación con del self[key].

__iter__(self): Define la iteración sobre los elementos.

__contains__(self, item): Define el comportamiento del operador in.

Ejemplos prácticos

Método __repr__

class Ejemplo:
    def __repr__(self):
        return 'Representación personalizada'

obj = Ejemplo()
print(obj)  # Salida: Representación personalizada

Método __bool__

class Bandera:
    def __bool__(self):
        return False

bandera = Bandera()
print(bool(bandera))  # Salida: False

Métodos de atributos

class ManejoAtributos:
    def __getattr__(self, nombre):
        print(f'Atributo {nombre} no encontrado')
    
    def __setattr__(self, nombre, valor):
        print(f'Setting {nombre} = {valor}')
        super().__setattr__(nombre, valor)
    
    def __delattr__(self, nombre):
        print(f'Deleting {nombre}')
        super().__delattr__(nombre)

obj = ManejoAtributos()
obj.nuevo = 42        # Salida: Setting nuevo = 42
print(obj.nuevo)      # Salida: 42
del obj.nuevo         # Salida: Deleting nuevo

Descriptor personalizado

class Descriptor:
    def __init__(self, valor_inicial=0):
        self.valor = valor_inicial
    
    def __get__(self, instancia, propietario):
        return self.valor
    
    def __set__(self, instancia, nuevo_valor):
        self.valor = nuevo_valor

class MiClase:
    atributo = Descriptor()

inst = MiClase()
inst.atributo = 100
print(inst.atributo)  # Salida: 100

Sobrecarga de operadores

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, otro):
        return Vector(self.x + otro.x, self.y + otro.y)
    
    def __repr__(self):
        return f'Vector({self.x}, {self.y})'

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)  # Salida: Vector(4, 6)

Clase contenedora

class ListaLimitada:
    def __init__(self, max_elementos):
        self.elementos = []
        self.max = max_elementos
    
    def __getitem__(self, indice):
        return self.elementos[indice]
    
    def __setitem__(self, indice, valor):
        if indice >= self.max:
            raise IndexError('Límite excedido')
        self.elementos.insert(indice, valor)
    
    def __len__(self):
        return len(self.elementos)

lista = ListaLimitada(5)
lista[0] = 'primero'
print(lista[0])  # Salida: primero
print(len(lista))  # Salida: 1

Etiquetas: Python clases métodos mágicos dunders sobrecarga de operadores

Publicado el 6-2 07:11