Apariencia
📙 Clase 14 — Métodos y atributos
Fase 3 · Programación Orientada a Objetos ⬅️ Volver al índice de clases
🎯 Qué aprendí
- Atributos de instancia (cada objeto los suyos) vs atributos de clase (compartidos).
- Métodos que leen, modifican y calculan sobre
self. - El patrón contador/ID automático, directo a tus registros de app.
📖 PARTE TEÓRICA
🧬 1. Atributo de instancia vs atributo de clase
python
class Tarea:
contador = 0 # atributo de CLASE: compartido por TODAS
def __init__(self, texto):
Tarea.contador += 1 # toco el de la clase (prefijo Tarea.)
self.id = Tarea.contador # atributos de INSTANCIA: de ESTE objeto
self.texto = texto
self.hecha = False
def completar(self):
self.hecha = True
t1 = Tarea("Comprar pan")
t2 = Tarea("Estudiar")
t2.completar()
print(t1.id, t1.hecha) # 1 False
print(t2.id, t2.hecha) # 2 True
print(Tarea.contador) # 2 ← el compartido: cuántas se crearon Tarea (clase) t1 t2
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ contador = 2 │◀──┬───▶│ id=1 │ │ id=2 │
│ (compartido) │ │ │ texto="pan…" │ │ texto="Estud…" │
└────────────────┘ │ │ hecha=False │ │ hecha=True │
lo ven ambos └────────────────┘ └────────────────┘| Atributo de instancia | Atributo de clase | |
|---|---|---|
| Se define | dentro de __init__ con self. | directo en el cuerpo de la clase |
| Pertenece a | cada objeto | la clase (compartido) |
| Uso típico | datos del registro (texto, hecha) | contadores, configuración común, constantes |
⚠️ Gotcha: si haces
self.contador = 99en una instancia, no cambias el de la clase — creas un atributo de instancia que lo "tapa" para ese objeto. Para el compartido, usa siempreTarea.contador.
🛠️ 2. Tres tipos de método (por lo que hacen)
python
class Carrito:
def __init__(self):
self.items = [] # composición: un objeto CONTIENE una lista
def agregar(self, nombre, precio): # 1) MODIFICA el estado
self.items.append({"nombre": nombre, "precio": precio})
def total(self): # 2) CALCULA a partir del estado
return sum(item["precio"] for item in self.items)
def resumen(self): # 3) PRESENTA el estado
return f"{len(self.items)} items — S/{self.total()}"
c = Carrito()
c.agregar("Teclado", 120)
c.agregar("Mouse", 45)
print(c.total()) # 165
print(c.resumen()) # 2 items — S/165💡 Fíjate cómo
resumen()llama aself.total(): los métodos se combinan entre sí a través deself. Y el estado (self.items) viaja gratis — ya no necesitas globals ni pasar la lista de función en función. Este es el adiós alglobalde la Clase 10.
🧪 Tip de entrevista: "¿Diferencia entre atributo de clase y de instancia?" → El de clase se define en el cuerpo y es compartido; el de instancia se crea con
self.y es propio de cada objeto. Si asignas víaself, "tapas" el de clase para esa instancia.
🖥️ EN TU APP DE ESCRITORIO
Cada fila de tu To-Do/agenda pasa de dict suelto a objeto con ID y comportamiento — y la GUI consume sus métodos de presentación:
python
class Tarea:
contador = 0
def __init__(self, texto):
Tarea.contador += 1
self.id = Tarea.contador # ¡cada tarea nace con su ID único!
self.texto = texto
self.hecha = False
def alternar(self): # para el checkbox de la GUI
self.hecha = not self.hecha
def etiqueta(self):
return f"{'✅' if self.hecha else '⬜'} {self.id}. {self.texto}"
# En la GUI: cada checkbox llama al método del objeto correcto
for tarea in tareas:
chk = ctk.CTkCheckBox(lista, text=tarea.etiqueta(),
command=lambda t=tarea: (t.alternar(), pintar()))💡 El
lambda t=tarea:de la Clase 10 + métodos de objeto = cada widget opera sobre su registro. Este es el patrón que escala.
🗄️ CON BASE DE DATOS (caso de uso)
El id autoincremental que acabas de implementar con Tarea.contador existe nativo en SQLite: INTEGER PRIMARY KEY AUTOINCREMENT. Cuando migres a BD, borras tu contador manual y dejas que la BD asigne los IDs — pero ya entiendes qué hace y por qué hace falta (identificar cada registro para editar/borrar el correcto).
sql
CREATE TABLE tareas (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- el "Tarea.contador" profesional
texto TEXT,
hecha INTEGER
)🏋️ EJERCICIOS CON SOLUCIÓN
Ejercicio 1 — Cuenta bancaria
Crea Cuenta con atributo saldo (inicia en 0) y métodos depositar(monto) y consultar() que devuelva "Saldo: S/X".
Ver solución
python
class Cuenta:
def __init__(self):
self.saldo = 0
def depositar(self, monto):
self.saldo += monto
def consultar(self):
return f"Saldo: S/{self.saldo}"
c = Cuenta()
c.depositar(100)
c.depositar(50)
print(c.consultar()) # Saldo: S/150Ejercicio 2 — ID automático
Agrega a Cuenta un número de cuenta automático (la primera es la 1, la segunda la 2…) usando un atributo de clase.
Ver solución
python
class Cuenta:
ultimas = 0 # atributo de CLASE
def __init__(self):
Cuenta.ultimas += 1
self.numero = Cuenta.ultimas
self.saldo = 0
a, b = Cuenta(), Cuenta()
print(a.numero, b.numero, Cuenta.ultimas) # 1 2 2Ejercicio 3 — Método que usa otro método
Agrega puede_retirar(monto) que devuelva True/False, y retirar(monto) que lo use antes de descontar.
Ver solución
python
class Cuenta:
def __init__(self):
self.saldo = 0
def depositar(self, monto):
self.saldo += monto
def puede_retirar(self, monto):
return monto <= self.saldo
def retirar(self, monto):
if not self.puede_retirar(monto): # un método llamando a otro vía self
return False
self.saldo -= monto
return True
c = Cuenta(); c.depositar(100)
print(c.retirar(30), c.saldo) # True 70
print(c.retirar(500), c.saldo) # False 70❓ Preguntas y respuestas (autoevaluación)
1. ¿Dónde se define un atributo de clase y quiénes lo ven?
En el cuerpo de la clase (fuera de
__init__); lo comparten todas las instancias.
2. ¿Qué pasa si haces self.contador = 99 cuando contador es de clase?
Creas un atributo de instancia que "tapa" al de clase solo para ese objeto; el compartido no cambia.
3. ¿Cómo llama un método a otro de la misma clase?
A través de
self:self.otro_metodo().
4. ¿Qué problema de la Fase 2 resuelve guardar estado en self?
El abuso de
global: el estado viaja con el objeto y cualquier método lo alcanza sin variables globales.
📎 Apuntes relacionados
- El nacimiento de la clase → Clase 13
- El
lambda t=tareaque conecta widget↔objeto → Clase 10 · Scope
➡️ Siguiente
Clase 15 · Herencia — el mecanismo con el que tu app será una ventana.