Fundamentos de Python

Programación orientada a objetos en Python

La POO organiza un programa alrededor de objetos que poseen estado y ofrecen comportamiento mediante interfaces claras.

¿Qué es la programación orientada a objetos en Python?

La POO modela un sistema como objetos que colaboran. Cada objeto combina estado y operaciones válidas, facilitando responsabilidades claras en aplicaciones grandes.

Los cuatro principios fundamentales

  • Encapsulación: reunir estado y comportamiento y controlar el acceso.
  • Abstracción: mostrar una interfaz útil y ocultar detalles innecesarios.
  • Herencia: especializar una clase solo ante una verdadera relación «es un».
  • Polimorfismo: usar la misma operación con objetos distintos y comportamiento apropiado.

Diseño del ejemplo

  1. BankAccount posee el saldo y valida depósitos.
  2. SavingsAccount reutiliza el contrato base.
  3. SavingsAccount sobrescribe month_end para aplicar interés.
  4. El bucle usa todos los objetos mediante la misma interfaz.
Consejo: Prefiere composición cuando un objeto solo usa otro; usa herencia únicamente si el hijo sustituye correctamente al padre.

Ejemplo básico

Ejecutar código →
main.py
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = float(balance)

    @property
    def balance(self):
        return self._balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self._balance += amount

    def month_end(self):
        return "No interest"


class SavingsAccount(BankAccount):
    def __init__(self, owner, balance, interest_rate):
        super().__init__(owner, balance)
        self.interest_rate = interest_rate

    def month_end(self):
        self._balance *= 1 + self.interest_rate
        return "Interest applied"


accounts = [
    BankAccount("Ada", 100),
    SavingsAccount("Lin", 200, 0.05),
]
accounts[0].deposit(50)

for account in accounts:
    status = account.month_end()
    print(f"{account.owner}: {account.balance:.2f} ({status})")

Resultado esperado

Ada: 150.00 (No interest)
Lin: 210.00 (Interest applied)

Los cuatro principios de POO en la práctica

Estos ejemplos muestran cómo Python aplica encapsulación, abstracción, herencia y polimorfismo en código cotidiano.

Encapsulación

Ejecutar código →

reunir estado y comportamiento y controlar el acceso.

encapsulation.py
class BankAccount:
    def __init__(self, balance=0):
        # Double underscores prevent accidental direct access.
        self.__balance = balance

    @property
    def balance(self):
        # Expose read access without exposing mutation.
        return self.__balance

    def deposit(self, amount):
        # Keep validation next to the state it protects.
        if amount <= 0:
            raise ValueError("Amount must be positive")
        self.__balance += amount


account = BankAccount(100)
account.deposit(50)
print(account.balance)  # 150

Abstracción

Ejecutar código →

mostrar una interfaz útil y ocultar detalles innecesarios.

abstraction.py
from abc import ABC, abstractmethod


class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount):
        # Callers know what pay does, not how each provider does it.
        pass


class CreditCard(PaymentMethod):
    def pay(self, amount):
        # The concrete class hides its payment implementation.
        return f"Paid ${amount:.2f} by card"


payment = CreditCard()
print(payment.pay(25))

Herencia

Ejecutar código →

especializar una clase solo ante una verdadera relación «es un».

inheritance.py
class Employee:
    def __init__(self, name):
        self.name = name

    def describe(self):
        return self.name


class Developer(Employee):
    def __init__(self, name, language):
        # Reuse initialization from the parent class.
        super().__init__(name)
        self.language = language

    def describe(self):
        # Extend inherited behavior instead of duplicating it.
        return f"{super().describe()} writes {self.language}"


print(Developer("Ada", "Python").describe())

Polimorfismo

Ejecutar código →

usar la misma operación con objetos distintos y comportamiento apropiado.

polymorphism.py
class EmailNotification:
    def send(self, message):
        return f"Email: {message}"


class SmsNotification:
    def send(self, message):
        return f"SMS: {message}"


def notify(channels, message):
    for channel in channels:
        # The same call selects behavior from each concrete object.
        print(channel.send(message))


channels = [EmailNotification(), SmsNotification()]
notify(channels, "Order shipped")

Cómo funciona

BankAccount encapsula el saldo. SavingsAccount hereda la interfaz y sobrescribe month_end. El bucle trata ambos objetos igual y Python elige el método adecuado en tiempo de ejecución.

Cambia los valores y ejecuta el programa en el compilador Python online de CodeUtility sin instalar Python.

Ejercicios prácticos

Modifica las entradas y prueba casos límite antes de usar conjuntos de datos mayores.

  1. Predice la salida antes de ejecutar.
  2. Controla entradas vacías o no válidas.
  3. Extrae la lógica a una función y añade pruebas.
Ejecutar en el IDE de Python →