Juan Diego Andrés PRADA··RAMÍREZ Entrar
Lección 2 de 7

Clases y objetos

Antes de empezar: tu entorno virtual (venv). Como este es el primer módulo, dejemos listo el entorno. Un entorno virtual es una carpeta aislada donde viven Python y las dependencias de este proyecto, sin contaminar tu sistema. Abre tu terminal en la carpeta del proyecto y ejecuta, paso a paso:

python -m venv .venv # crea el entorno en la carpeta .venv
source .venv/bin/activate # actívalo en macOS / Linux
# .venv\Scripts\activate # en Windows (PowerShell)
python -m pip install --upgrade pip

Cuando el entorno está activo verás (.venv) al inicio de la línea de la terminal. Para instalar una dependencia: pip install <paquete>. Para salir: deactivate. En este curso no necesitaremos paquetes externos al inicio (solo la librería estándar), pero quiero que el reflejo de crear y activar venv sea automático en ti.

¿Qué es?

Una clase es una plantilla que describe cómo es una cosa: qué datos guarda y qué comportamiento tiene. Un objeto (o instancia) es una cosa concreta creada a partir de esa plantilla. La clase es el plano; el objeto es la casa construida con ese plano.

La Programación Orientada a Objetos (POO) parte de una idea simple: agrupar los datos y el comportamiento que opera sobre ellos en una misma unidad. En programación estructurada sueles separar datos en diccionarios y lógica en funciones sueltas; la POO los hace vivir juntos, con reglas claras sobre quién toca qué.

¿Cómo funciona?

En Python defines una clase con class. El método especial __init__ es el constructor: se ejecuta automáticamente al crear cada objeto y sirve para inicializar sus datos. El primer parámetro de todo método es self, que es el objeto concreto sobre el que se llama el método. Los datos guardados en self se llaman atributos de instancia.

¿Para qué sirve?

Sirve para modelar el mundo del problema con piezas que se responsabilizan de sí mismas. En vez de un diccionario suelto que cualquiera puede romper con un typo, tienes un objeto que sabe validar y operar sobre sus propios datos.

Qué pieza del sistema construimos

El proyecto final es un Sistema de notas con POO. En este módulo construimos la primera versión de la clase Estudiante: guarda nombre y una lista de notas, permite agregar notas y calcular su promedio. Es el ladrillo sobre el que crecerá todo lo demás.

De diccionario a clase

Así modelaríamos un estudiante sin POO:

estudiante = {"nombre": "Ana", "notas": []}

def promedio(est):
 if not est["notas"]:
 return 0.0
 return sum(est["notas"]) / len(est["notas"])

Funciona, pero nada impide escribir estudiante["ntoas"] = [] (un typo silencioso) ni garantiza que la clave notas exista. La clase resuelve esto: define una sola vez la forma del objeto y el comportamiento asociado.

Construyendo la clase Estudiante

Vamos paso a paso.

class Estudiante:
 def __init__(self, nombre):
 self.nombre = nombre # atributo de instancia
 self.notas = [] # cada estudiante tiene su propia lista

Aquí __init__ recibe self (el objeto que se está creando) y nombre. Asignamos self.nombre y self.notas. Cada objeto tendrá su propia copia de esos atributos.

Ahora agregamos comportamiento: un método para registrar una nota y otro para el promedio.

class Estudiante:
 def __init__(self, nombre):
 self.nombre = nombre
 self.notas = []

 def agregar_nota(self, valor):
 self.notas.append(valor)

 def promedio(self):
 if not self.notas:
 return 0.0
 return sum(self.notas) / len(self.notas)

Fíjate: agregar_nota y promedio operan sobre self.notas, el dato del propio objeto. No reciben el estudiante por parámetro como en la versión con diccionario; ya viven dentro de él.

Creando y usando objetos

ana = Estudiante("Ana")
ana.agregar_nota(4.5)
ana.agregar_nota(3.0)

print(ana.nombre) # Ana
print(ana.promedio()) # 3.75

Estudiante("Ana") llama a __init__ por debajo: Python crea el objeto y le pasa "Ana" como nombre. Luego ana.agregar_nota(4.5) es lo mismo que Estudiante.agregar_nota(ana, 4.5): ese ana es el self.

Un atributo definido dentro de __init__ con self. es de instancia (uno por objeto). Un atributo definido directamente bajo class es de clase (compartido por todas las instancias). Para datos que cambian por objeto, usa siempre atributos de instancia.

Error común: poner una lista como valor por defecto en la firma del método, como def __init__(self, notas=[]). Esa lista se comparte entre todas las instancias y produce bugs muy difíciles de ver. Crea la lista dentro del cuerpo con self.notas = [].

Tech English: instance = instancia (objeto concreto), attribute = atributo, method = método, constructor = constructor (__init__).

Ejercicios

  1. Clase Curso. Diseña una clase Curso con atributos nombre y creditos, y un método descripcion() que devuelva una cadena como "Cálculo I (3 créditos)". Crea dos objetos Curso distintos y comprueba que cada uno conserva sus propios datos.

  2. Extiende Estudiante. Añade a Estudiante un método nota_maxima() que devuelva la nota más alta registrada, o None si aún no hay notas. Justifica por qué None es mejor que 0.0 en este caso.

Tu progreso se guarda en este navegador. Inicia sesión para guardarlo en tu cuenta y verlo desde cualquier dispositivo.