Skip to content

📙 Clase 13 — Clases y objetos

Fase 3 · Programación Orientada a Objetos ⬅️ Volver al índice de clases

🎯 Qué aprendí

  • Qué es una clase (el molde) y un objeto (lo fabricado con el molde).
  • __init__ (el constructor) y self (la referencia a "yo mismo").
  • Por qué la POO es el salto para tus apps: datos + comportamiento juntos.

📖 PARTE TEÓRICA

🏭 1. Clase = molde, objeto = instancia

Hasta ahora modelas un contacto como un diccionario suelto y sus acciones como funciones sueltas. La POO junta datos + comportamiento en una sola pieza: la clase.

python
class Contacto:                          # la CLASE: el molde
    def __init__(self, nombre, tel):     # el CONSTRUCTOR: cómo se fabrica uno
        self.nombre = nombre             # atributo: dato que vive EN el objeto
        self.tel = tel

    def descripcion(self):               # método: función que vive EN el objeto
        return f"{self.nombre} ({self.tel})"

# Crear OBJETOS (instancias): llamar a la clase como si fuera función
c1 = Contacto("Ana", "999-111")
c2 = Contacto("Luis", "988-222")

print(c1.descripcion())    # Ana (999-111)
print(c2.descripcion())    # Luis (988-222)   ← cada objeto tiene SUS datos
print(type(c1).__name__)   # Contacto
        CLASE (molde)                objetos (instancias)
   ┌───────────────────┐        c1 ▶ nombre="Ana",  tel="999-111"
   │ class Contacto:   │ ────▶  c2 ▶ nombre="Luis", tel="988-222"
   │   __init__        │        (mismas "piezas", valores propios)
   │   descripcion()   │
   └───────────────────┘

💡 Ya usas objetos hace semanas sin saberlo: ctk.CTkButton(...) es crear una instancia de la clase CTkButton. Cada botón es un objeto con sus propios datos (text, command…).

🐣 2. __init__: el constructor

__init__ corre automáticamente al crear el objeto. Es donde recibes los datos iniciales y los guardas como atributos.

python
class Contacto:
    def __init__(self, nombre, tel):
        #          └── los argumentos de Contacto("Ana", "999-111")
        self.nombre = nombre    # "guárdalo en MÍ"
        self.tel = tel

📌 Se llama dunder init (double underscore). No lo llamas tú: lo llama Python al hacer Contacto(...).

🪞 3. self: "yo mismo"

self es la referencia al objeto concreto sobre el que se está trabajando. Es el primer parámetro de todo método, y Python lo pasa solo:

python
c1.descripcion()
# Python lo traduce a:  Contacto.descripcion(c1)
#                                            └── ese es self

Por eso dentro del método, self.nombre significa "el nombre de este contacto" — c1 da "Ana" y c2 da "Luis" con el mismo código.

🧪 Tip de entrevista: "¿Qué es self?" → La referencia a la instancia actual. Permite que un método lea/modifique los atributos de ese objeto. Siempre es el primer parámetro (el nombre self es convención, no palabra reservada).

✏️ 4. Los atributos se leen y modifican con punto

python
print(c1.nombre)        # Ana        (leer)
c1.tel = "111-000"      # (modificar)
print(c1.descripcion()) # Ana (111-000)

⚠️ Olvidar self. dentro de un método (nombre = nombre en vez de self.nombre = nombre) crea una variable local que muere al salir — el objeto queda sin el dato. Error clásico de principiante en POO.


🖥️ EN TU APP DE ESCRITORIO

La POO reemplaza a los diccionarios sueltos como modelo de datos de tu app:

python
class Tarea:
    def __init__(self, texto):
        self.texto = texto
        self.hecha = False

    def completar(self):
        self.hecha = True

    def etiqueta(self):
        """El texto listo para mostrar en la GUI."""
        return f"✅ {self.texto}" if self.hecha else f"⬜ {self.texto}"

tareas = [Tarea("Comprar pan"), Tarea("Estudiar Python")]
tareas[1].completar()

for t in tareas:
    print(t.etiqueta())
# ⬜ Comprar pan
# ✅ Estudiar Python

Compara con el dict de la Clase 04:

dict {"texto": ..., "hecha": ...}clase Tarea
Datos
Comportamiento (completar, etiqueta)❌ funciones sueltas✅ vive junto a los datos
Errores de tipeot["hehca"] crea clave nueva 💥t.hehcaAttributeError claro
Autocompletado del editorpobrecompleto

💡 El dict sigue siendo perfecto para mover datos (formulario → BD). La clase gana cuando el dato tiene comportamiento. Ambos conviven: Tarea puede tener un método que se convierta a dict para guardarse.


🗄️ CON BASE DE DATOS (caso de uso)

El puente dict = fila (Clase 04) se extiende: objeto ↔ dict ↔ fila.

python
class Contacto:
    def __init__(self, nombre, tel):
        self.nombre = nombre
        self.tel = tel

    def a_dict(self):
        """Para INSERT con parámetros :nombre / :tel"""
        return {"nombre": self.nombre, "tel": self.tel}

c = Contacto("Ana", "999-111")
con.execute("INSERT INTO contactos VALUES (:nombre, :tel)", c.a_dict())

🗄️ Los ORMs profesionales (SQLAlchemy) hacen exactamente esto en grande: una clase por tabla, un objeto por fila. Ya estás entendiendo su fundamento.


🏋️ EJERCICIOS CON SOLUCIÓN

Ejercicio 1 — Tu primera clase

Crea la clase Producto con nombre y precio, y un método mostrar() que devuelva "Teclado: S/120". Crea dos productos y muéstralos.

Ver solución
python
class Producto:
    def __init__(self, nombre, precio):
        self.nombre = nombre
        self.precio = precio

    def mostrar(self):
        return f"{self.nombre}: S/{self.precio}"

p1 = Producto("Teclado", 120)
p2 = Producto("Mouse", 45)
print(p1.mostrar())   # Teclado: S/120
print(p2.mostrar())   # Mouse: S/45

Ejercicio 2 — Comportamiento

Agrega a Producto un método aplicar_descuento(porcentaje) que modifique su precio.

Ver solución
python
class Producto:
    def __init__(self, nombre, precio):
        self.nombre = nombre
        self.precio = precio

    def aplicar_descuento(self, porcentaje):
        self.precio = self.precio * (1 - porcentaje / 100)

p = Producto("Teclado", 120)
p.aplicar_descuento(25)
print(p.precio)   # 90.0

Ejercicio 3 — Encuentra el bug

python
class Usuario:
    def __init__(self, nombre):
        nombre = nombre
u = Usuario("Ana")
print(u.nombre)
Ver solución
python
# AttributeError: 'Usuario' object has no attribute 'nombre'
# Falta self.: 'nombre = nombre' asigna a una variable LOCAL que muere al salir.
# Correcto:
class Usuario:
    def __init__(self, nombre):
        self.nombre = nombre

❓ Preguntas y respuestas (autoevaluación)

1. ¿Qué diferencia hay entre clase y objeto?

La clase es el molde (definición); el objeto es una instancia creada con ese molde, con sus propios valores.

2. ¿Cuándo se ejecuta __init__?

Automáticamente al crear la instancia: Contacto("Ana", "999").

3. ¿Qué es self y quién lo pasa?

La referencia a la instancia actual; lo pasa Python solo al llamar objeto.metodo().

4. ¿Qué error da t.hehca (typo) en una clase vs t["hehca"] en un dict?

La clase lanza AttributeError al leer (falla claro y rápido); en el dict, si asignas t["hehca"]=True crea silenciosamente una clave nueva y el bug se esconde.


📎 Apuntes relacionados

➡️ Siguiente

Clase 14 · Métodos y atributos — atributos de clase vs de instancia.