Módulo 4: Introducción a la POO

Resumen rápido

Objetivo del módulo

Entender el paradigma de la Programación Orientada a Objetos (POO), su filosofía y cómo Python lo implementa mediante clases, objetos, herencia, encapsulamiento y polimorfismo.

¿Qué es la POO?

La Programación Orientada a Objetos organiza el código en torno a objetos, en lugar de hacerlo solo con funciones o estructuras de datos. Un objeto representa algo del mundo real (una persona, un coche, un producto…) y combina:

  • Atributos → sus características o datos.
  • Métodos → las acciones que puede realizar.

La clase es la plantilla; el objeto es la versión concreta, con valores reales. En el Tema 14 verás cómo definirlas paso a paso.

Los cuatro pilares de la POO

  • Encapsulamiento → proteger los datos internos del objeto y controlar cómo se accede a ellos (Temas 15 y 16).
  • Herencia → crear nuevas clases a partir de otras, reutilizando código (Tema 16).
  • Polimorfismo → objetos distintos responden de forma propia a un mismo método (Tema 16).
  • Abstracción → centrarse en lo esencial y ocultar los detalles internos (Tema 16).

¿Por qué usar POO?

La POO facilita:

  • Reutilización del código (herencia).
  • Mantenimiento más sencillo (modularidad).
  • Legibilidad y organización del código.
  • Modelar problemas complejos de forma natural.

En proyectos grandes, escribir código con POO es más escalable y flexible que hacerlo solo con funciones sueltas.

Tema 14: Clases y Objetos en Python

Objetivo del tema

Comprender qué son las clases y objetos en Python, cómo se definen y cómo se usan para modelar entidades del mundo real.

1. ¿Qué es la POO?

La Programación Orientada a Objetos (POO) es un paradigma que organiza el código en clases (plantillas) y objetos (instancias de esas clases).

  • Clase: Define atributos (datos) y métodos (funciones).
  • Objeto: Es una instancia de una clase, con valores propios.

Ejemplo de la vida real:

  • Clase: Coche (plantilla).
  • Objeto: "Mi coche rojo Toyota".

2. Definición de una clase básica en Python

3. Crear objetos a partir de la clase

4. Atributos y métodos

  • Atributos: Variables propias de cada objeto.
  • Métodos: Funciones definidas dentro de la clase.

Ejemplo con atributos adicionales:

5. Buenas prácticas

  • Usar nombres descriptivos para clases (Persona, Coche).
  • Por convención, los nombres de clases van con Mayúscula inicial (CamelCase).
  • Usar self para acceder a atributos y métodos dentro de la clase.

Actividades prácticas

  1. Crear una clase Animal con atributos nombre y especie.
  • Añadir un método presentarse() que muestre un mensaje con su nombre y especie.
  1. Crear una clase Rectangulo con atributos ancho y alto.
  • Añadir un método que calcule el área.
  1. Crear varios objetos de estas clases y probar sus métodos.

¿Qué es self?

self es una referencia al propio objeto (la instancia actual de la clase). Sirve para acceder a sus atributos y métodos desde dentro de la clase.

La respuesta corta es:

🔹 Solo debes usar self en los métodos de instancia, es decir, en aquellos métodos que operan sobre una instancia (objeto concreto) de la clase. 🔹 Pero no en los métodos estáticos o de clase.

Cuando defines un método normal dentro de una clase, Python automáticamente pasa la instancia como primer argumento. Por convención, ese primer parámetro se llama self (aunque podrías llamarlo de otra forma, no se recomienda).

Ejemplo:

Cuando haces:

Internamente Python ejecuta:

Por eso self representa al objeto p.

Cuándo sí usar self

Usa self en métodos de instancia, cuando necesitas acceder o modificar:

  • atributos de la instancia
  • otros métodos de la misma instancia

Ejemplo correcto:

Cuándo no usar self

No uses self en:

Métodos estáticos (@staticmethod)

No reciben self ni cls, porque no acceden a la instancia ni a la clase.

Métodos de clase (@classmethod)

Usan cls en lugar de self, porque se refieren a la clase, no a una instancia concreta.

Qué pasa si olvidas poner self

Si defines un método de instancia sin self y luego lo llamas desde un objeto, obtendrás un error como este:

TypeError: metodo() takes 0 positional arguments but 1 was given

Esto ocurre porque Python pasa automáticamente el objeto como primer argumento, aunque tú no lo hayas declarado.

En resumen

Tipo de método Decorador Primer parámetro Usa self Accede a atributos de instancia Accede a atributos de clase
Instancia ninguno self ✅ Sí
Clase @classmethod cls ❌ No
Estático @staticmethod ninguno ❌ No

¿Qué es un @classmethod?

Un @classmethod es un método que pertenece a la clase, no a una instancia concreta. Por eso, recibe como primer parámetro cls (no self), que representa la propia clase.

Se usa cuando un método necesita acceder o modificar variables o comportamientos de la clase entera, y no de un objeto individual.

Diferencia clave

  • Un método normal (self) trabaja con los atributos de una instancia (por ejemplo, el nombre o edad de una persona concreta).
  • Un @classmethod trabaja con atributos de clase (compartidos por todas las instancias).

Ejemplo práctico

Uso:

Cuándo usar @classmethod

Úsalo cuando:

  • Quieras modificar o acceder a variables de clase.
  • Necesites crear métodos alternativos de construcción (por ejemplo, crear un objeto desde un formato distinto, como un diccionario o una cadena JSON).

Ejemplo de método de construcción:

En resumen:

Usa @classmethod cuando necesites trabajar a nivel de clase, no de objeto. Se identifica porque su primer argumento es cls, y puede modificar o crear instancias de la clase.

¿Qué es un @staticmethod?

Un @staticmethod es un método que vive dentro de la clase por organización, pero no recibe self ni cls: no accede ni a la instancia ni a la clase. Es, en la práctica, una función normal agrupada dentro de la clase porque está relacionada con ella.

Diferencia rápida:

  • self → método de instancia (trabaja con un objeto concreto).
  • cls@classmethod (trabaja con la clase).
  • ninguno → @staticmethod (no necesita ni objeto ni clase; solo agrupa una utilidad).

Tema 15: Atributos y Métodos

Objetivo del tema

Comprender cómo se definen y utilizan los atributos (propiedades) y métodos (funciones) dentro de las clases en Python, distinguiendo entre los diferentes tipos y su ámbito de uso.

1. ¿Qué son los atributos y métodos?

En la Programación Orientada a Objetos (POO), las clases agrupan dos elementos fundamentales:

  • Atributos → representan las características o propiedades de un objeto.
  • Métodos → representan las acciones o comportamientos que un objeto puede realizar.

Por ejemplo:

2. Tipos de atributos

En Python, existen tres tipos de atributos:

Atributos de instancia

Son propios de cada objeto. Se definen dentro del método __init__ y se accede a ellos mediante self.

Cada instancia de Persona tendrá sus propios valores de nombre y edad.

Atributos de clase

Son compartidos por todas las instancias de la clase.

Si se modifica el valor de Persona.especie, afectará a todos los objetos que no tengan un valor propio.

Atributos privados y protegidos

Python usa convenciones para indicar el nivel de acceso:

Tipo Notación Descripción
Público nombre Accesible desde cualquier parte
Protegido _nombre Uso interno o por subclases (no forzado)
Privado __nombre Enmascarado, no accesible directamente desde fuera

Ejemplo:

3. Métodos

Los métodos son funciones definidas dentro de una clase. Siempre reciben al menos un parámetro: self, que representa la instancia actual.

Ejemplo:

Tipos de métodos

Métodos de instancia
  • Usan self.
  • Pueden acceder a los atributos y métodos del objeto.
Métodos de clase
  • Se declaran con el decorador @classmethod.
  • Usan el parámetro cls en lugar de self.
  • Pueden acceder y modificar atributos de clase, pero no de instancia.
Métodos estáticos
  • Se declaran con @staticmethod.
  • No reciben ni self ni cls.
  • Funcionan como funciones normales dentro de una clase.

4. Encapsulamiento con getters y setters

Para acceder o modificar atributos privados, se suelen usar métodos getters y setters:

También se puede usar el decorador @property para hacerlo más limpio:

5. Actividades prácticas

Ejercicio 1

Crea una clase Coche con:

  • Atributos: marca, modelo, color.
  • Métodos: arrancar() y detener(). Crea dos instancias y llama a ambos métodos.

Ejercicio 2

Crea una clase CuentaBancaria con un atributo privado __saldo y:

  • Método depositar(cantidad) que sume al saldo.
  • Método retirar(cantidad) que reste si hay suficiente dinero.
  • Método mostrar_saldo() que imprima el saldo.

Ejercicio 3

Crea una clase Calculadora con:

  • Un método estático sumar(a, b)
  • Un método estático multiplicar(a, b) Prueba ambos sin crear una instancia.

Evaluación del tema

Criterios de evaluación:

  • Comprende la diferencia entre atributos de clase e instancia.
  • Aplica correctamente el uso de self, @classmethod y @staticmethod.
  • Implementa atributos privados con sus métodos de acceso.
  • Crea clases limpias, coherentes y bien documentadas.

Tema 16: Herencia, Polimorfismo, Encapsulamiento y Abstracción

Objetivo del tema

Comprender los pilares fundamentales de la Programación Orientada a Objetos (POO): herencia, polimorfismo, encapsulamiento y abstracción, y aplicarlos para crear clases más reutilizables, extensibles y seguras.

1. Herencia

¿Qué es la herencia?

La herencia permite crear nuevas clases basadas en otras, reutilizando código y compartiendo comportamientos comunes. La clase base se llama superclase o clase padre, y la clase derivada se llama subclase o clase hija.

Ejemplo básico de herencia

super(): llamar al constructor de la clase padre

A veces, la clase hija necesita inicializar también atributos del padre. Para ello, se usa la función super():

Herencia múltiple

Python permite heredar de más de una clase. Se debe tener cuidado para evitar conflictos (orden de resolución de métodos – MRO).

2. Polimorfismo

¿Qué es?

El polimorfismo significa “muchas formas”. Permite usar el mismo método o interfaz para distintos tipos de objetos.

Aunque Perro y Gato son distintos, ambos implementan el método hablar(), y la función hacer_hablar() puede llamarlos indistintamente.

Polimorfismo en herencia

Las clases hijas pueden redefinir métodos del padre para adaptarlos a su propio comportamiento.

3. Encapsulamiento

¿Qué es?

El encapsulamiento protege los datos internos de una clase, evitando accesos directos desde fuera y controlando la modificación de sus valores.

En Python se logra mediante convenciones de nombres:

Tipo de atributo Notación Acceso
Público atributo Libre
Protegido _atributo Solo para la clase y subclases (por convención)
Privado __atributo Enmascarado (no accesible directamente)

Ejemplo de encapsulamiento

Propiedades (@property)

Permiten acceder a métodos como si fueran atributos, manteniendo el control sobre lectura y escritura.

4. Abstracción

¿Qué es la abstracción?

La abstracción consiste en centrarse en lo esencial y ocultar los detalles innecesarios: las clases representan qué hace algo, sin mostrar cómo lo hace internamente.

Cuando llamas a coche.arrancar(), no te interesa cómo funciona el motor internamente, solo que el coche arranca.

Python no tiene interfaces formales como Java o C#, pero sí tiene clases abstractas, y también puede simular interfaces de forma muy efectiva.

Clases abstractas

Una clase abstracta es una clase que no se puede instanciar directamente, sino que sirve como plantilla para que otras clases hereden de ella.

Define qué métodos deben implementarse, pero no necesariamente cómo.

Python ofrece el módulo abc (Abstract Base Classes):

Esta clase no se puede instanciar directamente:

Las subclases deben implementar el método hablar:

Aquí Animal actúa como una plantilla que obliga a que todas las clases hijas tengan un método hablar.

¿Y las interfaces?

Python no tiene una palabra reservada interface, pero se pueden simular las interfaces de dos maneras:

Opción 1: Usar una clase abstracta solo con métodos abstractos

Esta clase no tiene implementación, solo define un contrato: “Cualquier clase que herede de Dibujable debe implementar dibujar()”.

Aquí Dibujable funciona igual que una interfaz en Java o C#.

Opción 2: Usar duck typing (filosofía de Python)

Python es más flexible. Si un objeto tiene el método que se espera, no importa su tipo.

🦆 "Si camina como un pato y suena como un pato, probablemente es un pato."

No hay herencia ni interfaz formal, pero ambos objetos cumplen el “contrato” de tener el método volar().

Diferencias clave

Concepto ¿Existe en Python? ¿Cómo se implementa? Se puede instanciar
Clase abstracta class X(ABC) + @abstractmethod No
Interfaz No formalmente Clase abstracta solo con métodos abstractos No
Duck typing Implícitamente, por comportamiento

En resumen:

  • Clases abstractas → para definir una estructura base común.
  • Interfaces simuladas → usando clases abstractas sin implementación.
  • Duck typing → filosofía flexible sin restricciones formales.

Python combina la potencia de las clases abstractas con la flexibilidad del duck typing, por eso se considera un lenguaje orientado a objetos dinámico.

5. Combinando los tres conceptos

6. Actividades prácticas

Ejercicio 1: Herencia simple

Crea una clase base Vehiculo con atributos marca y modelo, y una clase hija Moto con un método hacer_caballito(). Instancia ambas clases y demuestra el uso de super().

Ejercicio 2: Polimorfismo

Crea una función emitir_sonido(animal) que reciba distintos objetos (Perro, Gato, Vaca), cada uno con un método sonido(), y muestre el resultado de forma polimórfica.

Ejercicio 3: Encapsulamiento

Crea una clase Cuenta con un atributo privado __saldo, métodos para depositar, retirar y mostrar_saldo, y usa @property para acceder y modificar el saldo de forma controlada.

Ejercicio 4: Abstracción

Crea una clase abstracta Figura (usando ABC y @abstractmethod) con un método abstracto area(), y dos subclases Circulo y Rectangulo que lo implementen. Comprueba que Figura no se puede instanciar directamente.

Evaluación del tema

Criterios de evaluación:

  • Comprende y aplica la herencia simple y múltiple correctamente.
  • Demuestra uso del polimorfismo en funciones o métodos sobrescritos.
  • Aplica encapsulamiento mediante atributos privados y @property.
  • Utiliza clases abstractas (abc) para definir contratos comunes.
  • Implementa código legible, organizado y coherente con la filosofía POO.

Tema 17: Métodos especiales (__init__, __str__, ...)

Objetivo del tema

Comprender qué son los métodos especiales o mágicos en Python, cómo y cuándo se ejecutan automáticamente, y cómo redefinirlos para personalizar el comportamiento de nuestras clases.

1. ¿Qué son los métodos especiales?

Los métodos especiales (también llamados dunder methods, por “double underscore”) son métodos internos de Python que permiten definir cómo se comportan los objetos en operaciones comunes como la creación, impresión, comparación o suma.

Todos comienzan y terminan con doble guion bajo, por ejemplo:

  • __init__(), contructor
  • __str__(), toString() en otros lenguajes
  • __len__(),
  • __add__(),

Estos métodos son invocados automáticamente por Python según la operación que se realice.

2. Método __init__() — Constructor

Se ejecuta automáticamente al crear una instancia de una clase. Sirve para inicializar los atributos del objeto.

Es equivalente al constructor en otros lenguajes (Java, C++, etc.).

3. Método __str__() — Representación en texto

Define cómo se muestra un objeto cuando se usa print() o str().

4. Método __repr__() — Representación técnica del objeto

__repr__() devuelve una representación más técnica o detallada, útil para depuración o desarrollo. Si no existe __str__(), Python usa __repr__() como alternativa.

5. Métodos de comparación

Permiten definir cómo se comparan los objetos entre sí.

Método Operación Ejemplo
__eq__ Igualdad (==) a == b
__ne__ Desigualdad (!=) a != b
__lt__ Menor que (<) a < b
__le__ Menor o igual (<=) a <= b
__gt__ Mayor que (>) a > b
__ge__ Mayor o igual (>=) a >= b

Ejemplo:

6. Métodos aritméticos

Permiten redefinir cómo se comportan los objetos ante operaciones matemáticas.

Método Operación Ejemplo
__add__ Suma (+) a + b
__sub__ Resta (-) a - b
__mul__ Multiplicación (*) a * b
__truediv__ División (/) a / b
__floordiv__ División entera (//) a // b
__mod__ Módulo (%) a % b

Ejemplo:

7. Otros métodos útiles

Método Descripción
__len__() Define el comportamiento de len(objeto)
__getitem__() Permite acceder a elementos como en una lista (obj[i])
__setitem__() Permite asignar valores (obj[i] = valor)
__del__() Destructor, se ejecuta al eliminar el objeto
__call__() Permite que un objeto sea llamado como función

Ejemplo con __len__() y __getitem__():

8. Beneficio de los métodos especiales

Los métodos mágicos permiten:

  • Hacer que las clases se comporten como tipos nativos de Python.
  • Crear interfaces intuitivas y expresivas.
  • Facilitar la depuración (__repr__, __str__).
  • Definir operaciones personalizadas sin romper la sintaxis del lenguaje.

9. Actividades prácticas

Ejercicio 1

Crea una clase Rectangulo con atributos ancho y alto. Redefine:

  • __str__() para mostrar “Rectángulo de XxY cm”.
  • __eq__() para que dos rectángulos sean iguales si tienen la misma área.

Ejercicio 2

Crea una clase Cuenta con:

  • Atributo saldo.
  • Redefine __add__ para sumar el saldo de dos cuentas.
  • Redefine __str__ para mostrar el saldo actual.
  • Prueba la suma de dos cuentas.

Ejercicio 3

Crea una clase ListaNumeros que:

  • Contenga una lista de números.
  • Implemente __len__ y __getitem__ para comportarse como una lista normal.
  • Permita recorrerla con un bucle for.

Evaluación del tema

Criterios de evaluación:

  • Comprende el propósito y uso de los métodos especiales.
  • Implementa correctamente __init__, __str__, y __repr__.
  • Define operaciones personalizadas (__add__, __eq__, etc.).
  • Aplica los métodos mágicos para hacer sus clases más intuitivas y reutilizables.