Skip to main content

Métodos dunder o mágicos

Dunder or Magic Methods in Python

Los métodos Dunder (doble guión bajo) o magic son métodos especiales en Python que permiten personalizar clases y objetos. Estos métodos se llaman mágicos porque pueden cambiar el comportamiento del código de formas inesperadas. Entender e implementar estos métodos puede mejorar enormemente la funcionalidad y flexibilidad de tus programas Python.

Construyendo Objetos y Expresiones

En Python, los objetos son instancias de clases, que definen los atributos y métodos del objeto. El proceso de crear un objeto en Python implica definir una clase, que especifica la estructura y el comportamiento del objeto, y luego crear instancias de esa clase.

Definición de Clases en Python

Para definir una clase en Python, se utiliza la palabra clave class, seguida del nombre de la clase. Por ejemplo, el siguiente código define una clase simple llamada Persona:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say_hello(self):
        print(f"Hello, my name is {self.name} and I am {self.age} years old.")

El método __init__ es un método especial que se llama cuando se crea una instancia de la clase. Inicializa los atributos del objeto.

Método mágico __init__ de Python

El método __init__ es un método mágico especial que se llama cuando se crea una instancia de una clase. Inicializa los atributos del objeto. En el ejemplo anterior, el método __init__ toma dos parámetros, name y age, que se utilizan para inicializar los atributos name y age del objeto.

Creando Instancias de Clases en Python

Para crear una instancia de una clase, se llama a la clase como si fuera una función, pasando cualquier argumento que el método __init__ dunder requiera. Por ejemplo, el siguiente código crea dos instancias de la clase Persona:

# Defining a car class
class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
    
    def describe_car(self):
        print(f"The car is a {self.year} {self.make} {self.model}.")
    
    
# Creating an instance of Car class
car1 = Car("Honda", "Accord", 2021)

# Calling the describe_car method
car1.describe_car()

# Output: The car is a 2021 Honda Accord.
# Defining a book class
class Book:
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages
    
    def describe_book(self):
        print(f"The book '{self.title}' is written by {self.author} and has {self.pages} pages.")
        

# Creating an instance of Book class
book1 = Book("The Alchemist", "Paulo Coelho", 208)

# Calling the describe_book method
book1.describe_book()

# Output: The book 'The Alchemist' is written by Paulo Coelho and has 208 pages.

Creación de objetos iteradores

Un iterador es un objeto que permite la iteración secuencial (bucle) sobre una colección de elementos, un elemento cada vez. En Python, puedes crear objetos iteradores usando clases o funciones.

Clase Generadora de Python

Puedes crear un iterador utilizando la clase generador en Python. La clase generador es un tipo de objeto que se utiliza para crear objetos iterables utilizando la sentencia yield.

class MyGenerator:
    def __init__(self):
        self.num = 0

    def __iter__(self):
        return self
    
    def __next__(self):
        if self.num <= 5:
            value = self.num
            self.num += 1
            return value
        else:
            raise StopIteration
def my_generator():
    num = 0
    while num <= 5:
        yield num
        num += 1

# Using the generator class
gen = MyGenerator()
for x in gen:
    print(x)

# Using the function generator
gen = my_generator()
for x in gen:
    print(x)

En este ejemplo, MyGenerator es una clase generadora que hereda de la clase incorporada object. Define un método __init__() que inicializa el atributo num a 0. También define el método __iter__() que devuelve el objeto iterador (self en este caso) y el método mágico __next__() que genera el siguiente valor de la secuencia.

También puedes crear un iterador usando un generador de funciones de Python. Un generador de funciones es una función que contiene la sentencia yield.

En este ejemplo, la función my_generator es un generador de funciones que utiliza la sentencia yield para generar el siguiente valor de la secuencia.

En los dos ejemplos anteriores, puedes crear un objeto iterador de la siguiente manera:

Ambos ejemplos de código mostrarán los valores 0, 1, 2, 3, 4, y 5 cuando se itere sobre ellos.

Manejo de referencias a atributos

Las referencias a atributos se utilizan para acceder a los atributos de un objeto en Python. Se puede acceder a ellas usando la sintaxis de la notación de puntos y también se puede acceder dinámicamente usando la función getattr().

La función getattr() toma dos argumentos - el objeto cuyo atributo necesita ser accedido y el nombre del atributo como cadena. Si no se encuentra el atributo, se produce un error AttributeError.


class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

my_dog = Dog("Max", "German Shepherd")
print(my_dog.name) ### Output

my_cat = {"name": "Fluffy", "breed": "Persian"}
cat_name = getattr(my_cat, "name")
print(cat_name) ### Output

En el primer caso, creamos una clase Dog y accedemos al atributo name utilizando la sintaxis de notación por puntos.

En el segundo caso, creamos un objeto diccionario mi_gato y accedemos al atributo nombre dinámicamente usando la función getattr(). Almacenamos el valor del atributo en cat_name y lo imprimimos.

Representando Objetos como Cadenas con el Método Mágico

En Python, podemos representar objetos como cadenas usando el método __repr__(). Este método es llamado cuando usamos la función repr() o cuando imprimimos un objeto usando la función print().

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __repr__(self):
        return f"Point({self.x}, {self.y})"
        
p = Point(2, 3)
print(p)  ### Output

En el código anterior, hemos definido una clase Point con atributos x y y. También hemos definido un método __repr__() dunder que devuelve una representación de cadena del objeto Point. Cuando imprimimos el objeto p, éste llama al método mágico __repr__() para obtener su representación en cadena.

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        
    def __repr__(self):
        return f"Car(make={self.make}, model={self.model}, year={self.year})"
        
c = Car("Toyota", "Camry", 2021)
print(c)  ### Output

En este ejemplo, hemos definido una clase Car con los atributos make, model y year. También hemos definido un método __repr__() que devuelve una representación en forma de cadena del objeto Car. Cuando imprimimos el objeto c, éste llama al método __repr__() para obtener su representación en cadena.

Limpiando Objetos con el Método Dunder

En Python, los objetos son automáticamente recolectados cuando ya no son necesarios. Sin embargo, a veces puede ser necesario definir acciones de limpieza adicionales para un objeto. Esto puede hacerse usando el método __del__, que es llamado cuando el objeto está a punto de ser destruido.

Este método dunder es útil para liberar recursos como archivos, conexiones de red, u otros objetos a nivel de sistema que no son gestionados automáticamente por Python.

class MyClass:
    def __init__(self):
        self.file = open('example.txt', 'r')

    def __del__(self):
        self.file.close()

En este ejemplo, el constructor MyClass crea un objeto fichero y lo almacena en la variable de instancia file. Cuando se destruye el objeto, se llama al método __del__, que cierra el fichero.

Realizando Comparaciones con Métodos Dunder

Python proporciona múltiples formas de comparar valores, variables o expresiones. Algunos operadores comúnmente usados para realizar comparaciones incluyen ==, !=, >, <, >=, <=, in, y is.

Python Compara Cadenas

El método __lt__() se utiliza para implementar el operador de comparación menor que en Python. Devuelve True si la primera cadena es menor que la segunda y False en caso contrario.

string1 = "apple"
string2 = "banana"
if string1.__lt__(string2):
    print("string1 is less than string2")
else:
    print("string1 is greater than or equal to string2")

# Output:
#string1 is less than string2
fruits = ["apple", "banana", "orange", "kiwi"]
sorted_fruits = sorted(fruits, key=lambda x: x.__lt__("c"))
print(sorted_fruits)
# Output:

# ['orange', 'kiwi', 'apple', 'banana']

En el ejemplo anterior, hemos ordenado la lista de frutas en orden ascendente en función de si el primer carácter de cada cadena es menor o mayor que c. lambda x: x.lt(c)devuelveVerdaderosi el primer carácter dexes menor quecyFalso` en caso contrario.

¡Contribuya con nosotros!

No dudes en contribuir a los tutoriales de Python en GitHub: crea un fork, actualiza el contenido y emite un pull request.

Profile picture for user AliaksandrSumich
Python engineer, expert in third-party web services integration.
Actualizado: 05/03/2024 - 22:51
Profile picture for user angarsky
Revisado y aprobado