0%
Programación Orientada a Objetos en Python

POO

Estructura tu código con objetos y clases

10-15 min

Programación Orientada a Objetos en Python

La Programación Orientada a Objetos (POO) es un paradigma de programación que organiza el código en torno a objetos en lugar de funciones y lógica. Python, al ser un lenguaje multiparadigma, ofrece un soporte completo y elegante para la POO. En este tutorial vamos a explorar los conceptos fundamentales de la POO en Python y a entender cómo utilizarlos de forma eficaz en tus programas.

¿Por qué usar la POO?

Antes de entrar en los detalles, es importante entender por qué la POO es tan popular:

  1. Organización: la POO permite organizar tu código de una manera más estructurada y modular.
  2. Reutilización: facilita la reutilización del código gracias a la herencia y a la composición.
  3. Mantenibilidad: un código bien estructurado en objetos suele ser más fácil de mantener y de hacer evolucionar.
  4. Encapsulación: la POO permite ocultar la complejidad y proteger los datos gracias a la encapsulación.

Clases y objetos

Los dos conceptos fundamentales de la POO son las clases y los objetos.

Clases

Una clase es un modelo o un plano para crear objetos. Define los atributos (variables) y los métodos (funciones) que tendrán los objetos.

Así se define una clase sencilla en Python:

class Personne:
    def __init__(self, nom, age):
        self.nom = nom
        self.age = age
    
    def saluer(self):
        return f"Bonjour, je m'appelle {self.nom} et j'ai {self.age} ans."

En este ejemplo:

  • Personne es el nombre de la clase.
  • __init__ es un método especial (constructor) que inicializa un nuevo objeto.
  • self hace referencia a la instancia del objeto actual.
  • nom y age son atributos.
  • saluer es un método.

Objetos

Un objeto es una instancia de una clase. Para crear un objeto, se llama a la clase como si fuera una función:

personne1 = Personne("Alice", 30)
personne2 = Personne("Bob", 25)

print(personne1.saluer())  # Muestra: Bonjour, je m'appelle Alice et j'ai 30 ans.
print(personne2.saluer())  # Muestra: Bonjour, je m'appelle Bob et j'ai 25 ans.

Los objetos personne1 y personne2 son instancias distintas de la clase Personne, cada una con sus propios valores de atributos.

Métodos especiales

Python ofrece varios métodos especiales (también llamados métodos mágicos o métodos dunder, por “double underscore”) que permiten personalizar el comportamiento de los objetos.

class Livre:
    def __init__(self, titre, auteur, pages):
        self.titre = titre
        self.auteur = auteur
        self.pages = pages
        self.page_actuelle = 0
    
    def __str__(self):
        """Define cómo se representa el objeto como cadena de texto."""
        return f"{self.titre} par {self.auteur}"
    
    def __len__(self):
        """Define el comportamiento cuando se aplica la función len() al objeto."""
        return self.pages
    
    def __del__(self):
        """Método llamado cuando se elimina el objeto."""
        print(f"Le livre {self.titre} a été fermé.")

livre = Livre("Python Facile", "J. Dupont", 200)
print(livre)           # Utiliza __str__
print(len(livre))      # Utiliza __len__
del livre              # Activa __del__

Herencia

La herencia permite que una clase herede los atributos y métodos de otra clase. Es un concepto fundamental para fomentar la reutilización del código.

class Animal:
    def __init__(self, nom):
        self.nom = nom
    
    def parler(self):
        pass  # Método que se sobrescribirá en las clases derivadas

class Chien(Animal):
    def parler(self):
        return f"{self.nom} dit Woof!"

class Chat(Animal):
    def parler(self):
        return f"{self.nom} dit Meow!"

animaux = [Chien("Rex"), Chat("Misty")]
for animal in animaux:
    print(animal.parler())

En este ejemplo:

  • Animal es la clase base (o clase padre).
  • Chien y Chat son clases derivadas (o clases hijas) que heredan de Animal.
  • El método parler se sobrescribe (se redefine) en cada clase derivada.

Herencia múltiple

A diferencia de otros lenguajes de programación, Python admite la herencia múltiple, lo que permite que una clase herede de varias clases padre.

class A:
    def methode_a(self):
        return "Méthode de A"

class B:
    def methode_b(self):
        return "Méthode de B"

class C(A, B):  # Hereda de A y B
    def methode_c(self):
        return "Méthode de C"

objet = C()
print(objet.methode_a())  # Heredado de A
print(objet.methode_b())  # Heredado de B
print(objet.methode_c())  # Definido en C

Encapsulación

La encapsulación es el concepto de agrupar los datos y los métodos que operan sobre ellos, y de restringir el acceso directo a las variables de un objeto.

En Python, la encapsulación se logra por convención más que mediante una aplicación estricta:

class CompteBancaire:
    def __init__(self, solde_initial):
        self._solde = solde_initial  # Convención: atributo protegido (un guion bajo)
        self.__compte_id = "123456"  # Convención: atributo privado (doble guion bajo)
    
    def deposer(self, montant):
        self._solde += montant
    
    def retirer(self, montant):
        if montant <= self._solde:
            self._solde -= montant
            return True
        return False
    
    def get_solde(self):
        return self._solde

compte = CompteBancaire(1000)
compte.deposer(500)
print(compte.get_solde())  # 1500
print(compte._solde)       # Accesible, pero por convención no debería accederse directamente
# print(compte.__compte_id)  # Error: renombrado por "name mangling"
print(compte._CompteBancaire__compte_id)  # Accesible mediante mangling

En Python:

  • Un atributo con un solo guion bajo (_solde) indica que está protegido (convención, no se aplica).
  • Un atributo con doble guion bajo (__compte_id) está sujeto al “name mangling” (renombrado), lo que hace más difícil acceder a él por accidente, pero no es realmente privado.

Polimorfismo

El polimorfismo permite tratar objetos de distintas clases como objetos de una clase común. En Python, el polimorfismo es intrínseco al lenguaje gracias al duck typing.

def faire_parler(animal):
    print(animal.parler())

chien = Chien("Rex")
chat = Chat("Misty")

faire_parler(chien)  # Rex dit Woof!
faire_parler(chat)   # Misty dit Meow!

La función faire_parler funciona con cualquier objeto que tenga un método parler, sin preocuparse de su clase concreta.

Propiedades

Las propiedades permiten controlar el acceso a los atributos de una clase manteniendo una interfaz limpia.

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius
    
    @property
    def celsius(self):
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("La température ne peut pas être inférieure au zéro absolu.")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32
    
    @fahrenheit.setter
    def fahrenheit(self, value):
        self.celsius = (value - 32) * 5/9

temp = Temperature()
temp.celsius = 25
print(f"Celsius: {temp.celsius}°C, Fahrenheit: {temp.fahrenheit}°F")

temp.fahrenheit = 77
print(f"Celsius: {temp.celsius}°C, Fahrenheit: {temp.fahrenheit}°F")

Métodos y atributos de clase

A diferencia de los métodos y atributos de instancia, que son propios de cada objeto, los métodos y atributos de clase son compartidos por todas las instancias de la clase.

class Voiture:
    # Atributo de clase
    nombre_de_voitures = 0
    
    def __init__(self, marque, modele):
        self.marque = marque
        self.modele = modele
        # Incrementar el atributo de clase
        Voiture.nombre_de_voitures += 1
    
    @classmethod
    def get_nombre_de_voitures(cls):
        return cls.nombre_de_voitures
    
    @staticmethod
    def est_electrique(marque, modele):
        # Un método estático que no depende del estado de la instancia
        # Lista ficticia de coches eléctricos
        voitures_electriques = [("Tesla", "Model 3"), ("Nissan", "Leaf")]
        return (marque, modele) in voitures_electriques

voiture1 = Voiture("Toyota", "Corolla")
voiture2 = Voiture("Honda", "Civic")

print(Voiture.get_nombre_de_voitures())  # 2
print(Voiture.est_electrique("Tesla", "Model 3"))  # True

Conclusión

La programación orientada a objetos en Python ofrece una forma potente y flexible de organizar tu código. Te permite crear programas más modulares, reutilizables y mantenibles.

En este tutorial hemos cubierto los conceptos fundamentales de la POO en Python, incluidos las clases, los objetos, la herencia, el polimorfismo y la encapsulación. ¡Pero esto es solo el principio! Existen muchos otros conceptos avanzados y patrones de diseño que pueden ayudarte a sacar el máximo partido a la POO.

En el próximo tutorial exploraremos la gestión de excepciones en Python, un aspecto crucial para escribir programas robustos.

Comentarios

Los comentarios funcionan con GitHub Discussions

Inicia sesión con GitHub para participar en la discusión

¡Enlace copiado!