01Recuperamos lo aprendido
BLOQUE 1
1 / 52
Clase 2 · Programación

Relacionar
Objetos

La clase pasada aprendimos a pensar en objetos.
Hoy vamos a hacer que trabajen juntos.

🧬 super()🪜 varios niveles📦 return🎁 objetos adentro de objetos

La frase de la clase de hoy

Ya sabemos pensar en objetos.

Ahora vamos a aprender a relacionarlos.

Un objeto solo no hace un programa.
Un programa es un montón de objetos hablando entre ellos.

Calentamiento · 3 minutos
☕

Una cafetería, sábado a la mañana

¿Qué objetos hay acá?

🧍 Cliente🧑‍🍳 Mozo🧾 Pedido 🪑 Mesa☕ Café💰 Caja📋 Menú

Elegimos uno: el Cliente

¿Qué información tiene?

nombremesadinero

¿Qué puede hacer?

pedir()pagar()irse()

Datos → atributos  ·  Acciones → métodos.
Las mismas dos preguntas de la clase pasada.

Quiz relámpago · a mano alzada

Tres preguntas, tres segundos cada una

Perro es una clase. firulais ¿qué es?

Un objeto. La clase es el molde; el objeto es lo fabricado.

¿Un Gato puede heredar de Animal?

Sí: un gato ES UN animal. Hereda todo lo que Animal ya tenía.

¿Y una Rueda puede heredar de Auto?

No. Una rueda no es un auto… está adentro del auto. 👀

Dónde quedamos la clase pasada

Sabemos hacer esto…

✓ Ya podemos

Crear una clase y objetos.

Darles atributos y métodos.

Hacer que una clase herede de otra.

✗ Todavía no

Que el hijo agregue sus propios atributos sin repetir los del padre.

Que un método nos devuelva un resultado para seguir usándolo.

Que un objeto tenga adentro a otro objeto.

El plan de hoy: cuatro herramientas

🧬

super()

El hijo aprovecha lo que el padre ya hace.

🪜

Varios niveles

Heredar de quien ya heredó.

📦

return

Que el método me entregue el resultado.

🎁

Composición

Un objeto adentro de otro objeto.

Y al final las usamos las cuatro juntas en un mismo programa.

Bloque 2 · Herramienta 1

Estamos armando un juego

Tenemos una clase Persona.
Ahora queremos una clase Jugador.

Un jugador… ¿es una persona?

Persona
  • nombre
  • edad
Jugador 🎮
  • nombre
  • edad
  • nivel

Si Jugador hereda de Persona… ¿tenemos que volver a escribir nombre y edad?

class Persona:
    def __init__(self, nombre, edad):
        self.nombre = nombre
        self.edad = edad

✗ Primer intento

class Jugador(Persona):
    def __init__(self, nombre, edad, nivel):
        self.nombre = nombre
        self.edad = edad
        self.nivel = nivel

Funciona… pero escribimos lo mismo dos veces.

El problema de verdad

Ahora imaginemos que mañana…

…queremos que toda Persona tenga también dni.

¿En cuántos lugares hay que tocar el código?

2

Con 1 hijo

6

Con 5 hijos

😱

Y alguno te lo vas a olvidar

¿Y si le pedimos al padre que haga la parte que él ya sabe hacer?

El padre hace

nombre
edad

+

El hijo agrega

nivel

=

Un jugador completo

sin repetir
nada

🧬 La herramienta

Se llama super()

class Persona:
    def __init__(self, nombre, edad):
        self.nombre = nombre
        self.edad = edad


class Jugador(Persona):
    def __init__(self, nombre, edad, nivel):
        super().__init__(nombre, edad)
        self.nivel = nivel

super() quiere decir
“la clase de arriba”.

Cómo se lee esa línea

“Padre, encargate vos de nombre y edad. Yo me ocupo de nivel.”

¿Qué pasa, paso a paso?

jugador = Jugador("Pedro", 28, 5)
Paso 1Python entra al __init__ de Jugador.
Paso 2super().__init__(nombre, edad) sube a Persona y guarda nombre y edad.
Paso 3Vuelve a Jugador y guarda nivel.
jugador.nombre → "Pedro" jugador.edad → 28 jugador.nivel → 5

🧬

El padre hace lo que ya sabe hacer.

El hijo agrega lo que necesita.

Predicción · antes de ejecutar

¿Y si me olvido de super()?

class Jugador(Persona):
    def __init__(self, nombre, edad, nivel):
        # me olvidé del super()
        self.nivel = nivel


jugador = Jugador("Pedro", 28, 5)
print(jugador.nombre)

¿Qué va a pasar en la última línea?

AttributeError: 'Jugador' object has no attribute 'nombre'

Por qué

Nadie ejecutó el __init__ de Persona.
El padre nunca hizo su parte.

Mini desafío · 4 minutos

Ahora ustedes: Vehiculo → Moto

class Vehiculo:
    def __init__(self, marca, ruedas):
        self.marca = marca
        self.ruedas = ruedas

Escribí Moto, que hereda de Vehiculo y agrega cilindrada.

Una solución

class Moto(Vehiculo):
    def __init__(self, marca, ruedas, cilindrada):
        super().__init__(marca, ruedas)
        self.cilindrada = cilindrada

Bonus: ¿y si todas las motos tienen 2 ruedas?
super().__init__(marca, 2) ← el hijo puede decidir qué le pasa al padre.

Bloque 3 · Herramienta 2

¿Un perro es un mamífero?

¿Un mamífero es un animal?

Entonces… ¿qué cosas recibe el perro?

Animal
Mamífero
Perro 🐶

Cada escalón suma, no reemplaza

Animal aporta

nombrecomer()

Mamífero agrega

pelajeamamantar()

Perro agrega

razaladrar()

Entonces un Perro tiene

nombrepelajeraza comer()amamantar()ladrar()
🪜 Ahora sí, al código
class Animal:
    def __init__(self, nombre):
        self.nombre = nombre


class Mamifero(Animal):
    def __init__(self, nombre, pelaje):
        super().__init__(nombre)
        self.pelaje = pelaje


class Perro(Mamifero):
    def __init__(self, nombre, pelaje, raza):
        super().__init__(nombre, pelaje)
        self.raza = raza

¿Qué hay de nuevo acá?

Nada

Es exactamente lo mismo del bloque anterior, dos veces.
Perro le pide a Mamifero, y Mamifero le pide a Animal.

Cada clase solo se ocupa de lo suyo. Nadie repite el trabajo de otro.

Predicción · antes de ejecutar

¿Qué imprime cada línea?

firu = Perro("Firulais", "corto", "beagle")

print(firu.nombre)
print(firu.pelaje)
print(firu.raza)
Firulais # lo puso Animal corto # lo puso Mamifero beagle # lo puso Perro

Lo importante

Los tres atributos viven en un solo objeto.
Al usarlo, no se nota quién puso cada cosa.

Una advertencia

Que se pueda… no significa que convenga

✗ Nadie necesita esto

SerVivo
  → Animal
    → Vertebrado
      → Mamifero
        → Canido
          → Perro
            → Beagle
              → BeagleChico

¿Cuál es el problema de una cadena tan larga?

Para entender una clase hay que leer ocho.
Y para encontrar un dato, subir ocho escalones.

Regla práctica: dos o tres niveles alcanzan casi siempre. Si necesitás más, casi seguro no era herencia lo que buscabas.

Mini desafío · 3 minutos

¿Cuál de estas cadenas tiene sentido?

Instrumento
  → Cuerda
    → Guitarra
✓Cada uno es un anterior
Empleado
  → Gerente
    → Oficina
✕Una oficina no es un gerente
Personaje
  → Enemigo
    → Jefe Final
✓El jefe es un enemigo especial

El test no cambió nunca: “¿ES UN?” en cada escalón.

Bloque 4 · Herramienta 3

Miremos esta función

def sumar(a, b):
    print(a + b)


sumar(5, 3)

¿Funciona?

8

Sí. Aparece el 8 en pantalla y todos contentos.

Predicción · antes de ejecutar

Ahora quiero guardar ese resultado

def sumar(a, b):
    print(a + b)


resultado = sumar(5, 3)
print(resultado)

¿Qué imprime la última línea?

8 None

El 8 se vio… pero resultado quedó vacío.

¿Por qué resultado quedó vacío, si el 8 apareció en la pantalla?

Porque

La función mostró el 8, pero nunca lo entregó.

Mostrar y entregar
no son lo mismo.

📦 La herramienta

Se llama return

✗ Antes

def sumar(a, b):
    print(a + b)
8 None

✓ Ahora

def sumar(a, b):
    return a + b
8
resultado = sumar(5, 3)   # resultado vale 8

La diferencia, dibujada

print() ↓ PANTALLA

Lo veo… y se terminó ahí.

return ↓ RESULTADO
  • ✅ puedo guardarlo
  • ✅ puedo compararlo
  • ✅ puedo usarlo en otra cuenta
  • ✅ puedo devolverlo de nuevo

print() me muestra algo. return me entrega algo.

🧑‍🍳

print()

El mozo te dice: “el café sale 2000”.

Lo escuchaste. Pero no tenés nada en la mano.

return

El mozo te trae el café.

Ahora sí: podés tomarlo, compartirlo, guardarlo.

Ahora en un objeto

Un método que devuelve un valor

class Jugador:
    def __init__(self, nombre, vida):
        self.nombre = nombre
        self.vida = vida

    def esta_vivo(self):
        return self.vida > 0
if jugador.esta_vivo():
    print("Seguí jugando")
else:
    print("Game Over")

Acá está la gracia

Con return, el objeto puede contestarle al programa.
Y con esa respuesta, el programa decide.

Mini desafío · a mano alzada

¿Acá necesitamos print() o return?

Caso 1Un método que calcula el precio final con IVA.

return — después quiero sumarlo al total.

Caso 2Un método que saluda al usuario al entrar al juego.

print() — solo hay que verlo, nadie lo reutiliza.

Caso 3Un método que dice si al jugador le alcanza la plata.

return — la respuesta va adentro de un if.

Caso 4Un método que muestra el menú de opciones.

print() — es información para la persona, no para el programa.

Bloque 5 · Herramienta 4
🧍⚔️

Volvemos al juego

¿Un Jugador ES un Arma?

✕No. Son cosas distintas.

¿Un Jugador TIENE un Arma?

✓Sí. Eso sí.

Entonces acá no va herencia.
Va otra cosa.

Las dos formas de relacionar objetos

🧬 ES UN

HERENCIA

Animal
Perro 🐶

El perro es un animal.
Recibe lo que el animal ya tenía.

🎁 TIENE UN

COMPOSICIÓN

Jugador 🧍
tiene
Arma ⚔️

El jugador tiene un arma.
Son dos objetos distintos, conectados.

Actividad · a mano alzada

Leelo en voz alta y decidí

1Un Gato y un Animal

ES UN → herencia

2Un Auto y un Motor

TIENE UN → composición

3Una Casa y una Puerta

TIENE UNA → composición

4Un Profesor y una Persona

ES UNA → herencia

5Un Celular y una Bateria

TIENE UNA → composición

Paso 1 de 3

Primero, el arma sola

class Arma:
    def __init__(self, nombre, daño):
        self.nombre = nombre
        self.daño = daño

    def atacar(self):
        return self.daño

¿Por qué return y no print?

Porque el daño no es para mirarlo:
es para restárselo a la vida del enemigo.

Un arma es un objeto completo: tiene sus datos y sabe hacer lo suyo. Todavía no sabe nada de ningún jugador.

Paso 2 de 3

Ahora el jugador… recibe un arma

class Jugador:
    def __init__(self, nombre, arma):
        self.nombre = nombre
        self.arma = arma

Esa línea resaltada… ¿qué guarda?

Guarda

Un objeto entero, no un texto ni un número.
Un atributo también puede ser otro objeto.

Es la misma línea de siempre: self.algo = algo. Lo único distinto es qué estamos guardando.

Paso 3 de 3

Y ahora los conectamos

espada = Arma("Espada", 20)

jugador = Jugador("Pedro", espada)

¿Qué pasa en la segunda línea?

El objeto espada entra adentro del objeto jugador.

Primero fabricamos el arma. Después fabricamos el jugador con el arma.

Cómo queda esto por dentro

jugador
  • nombre → "Pedro"
  • arma → ◆
apunta a
espada
  • nombre → "Espada"
  • daño → 20

Un objeto puede tener otro objeto adentro.

El punto significa “de”

¿Y cómo lo uso?

print(jugador.nombre)
# Pedro

print(jugador.arma.nombre)
# Espada

print(jugador.arma.atacar())
# 20

¿Cómo se lee jugador.arma.nombre?

“El nombre del arma del jugador.”

Se lee de derecha a izquierda, poniendo “de” en cada punto.

El jugador no sabe cuánto daño hace. Le pregunta al arma.

Por qué esto es tan útil

El jugador cambia de arma… y no tocamos nada

hacha = Arma("Hacha", 35)

jugador.arma = hacha

print(jugador.arma.atacar())
# 35

¿Qué línea de la clase Jugador tuvimos que cambiar?

Ninguna.

El jugador siempre supo lo mismo: “tengo un arma y le pido que ataque”. Qué arma sea, no es su problema.

Con 50 armas distintas, la clase Jugador sigue igual de corta.

Bloque 6 · Todo junto

🎮

Ahora las usamos las cuatro juntas.

Un mismo sistema con herencia, super(), return y composición.
Primero lo pensamos. Después lo dibujamos. Recién después lo escribimos.

Primero pensamos · sin código

Tres clases: Personaje, Jugador, Arma

¿Qué tiene todo personaje?

nombrevidaesta_vivo()recibir_daño()

¿Qué agrega el jugador?

armaatacar_a()

¿Y el arma?

nombredañoatacar()

En Personaje va solo lo que comparten todos. El arma no va ahí: no todos los personajes tienen.

Después lo dibujamos
Personaje
  • nombre
  • vida
  • esta_vivo()
  • recibir_daño()
Jugador 🧍
  • arma
  • atacar_a()
tiene
Arma ⚔️
  • nombre
  • daño
  • atacar()
🧬 Jugador ES UN Personaje 🎁 Jugador TIENE UN Arma
Paso 1 · la clase de arriba
class Personaje:
    def __init__(self, nombre, vida):
        self.nombre = nombre
        self.vida = vida

    def esta_vivo(self):
        return self.vida > 0

    def recibir_daño(self, cantidad):
        self.vida = self.vida - cantidad

Todo lo que comparten todos los personajes: jugadores, enemigos, jefes.

¿Por qué esta_vivo() usa return y recibir_daño() no?

esta_vivo() contesta una pregunta → hay que devolver la respuesta.
recibir_daño() hace un cambio en el objeto → no tiene nada que entregar.

Paso 2 · el hijo, con arma
class Jugador(Personaje):
    def __init__(self, nombre, vida, arma):
        super().__init__(nombre, vida)
        self.arma = arma

    def atacar_a(self, otro):
        daño = self.arma.atacar()
        otro.recibir_daño(daño)
        return daño

Línea 3: el padre pone nombre y vida.
Línea 4: el hijo agrega su arma.

En atacar_a(), ¿cuántos objetos participan?

Tres. El jugador le pide el daño a su arma, y se lo aplica al otro personaje.

Predicción · antes de ejecutar

Jugamos una partida

espada = Arma("Espada", 20)

pedro = Jugador("Pedro", 100, espada)
orco  = Personaje("Orco", 50)

pedro.atacar_a(orco)
print(orco.vida)

pedro.atacar_a(orco)
pedro.atacar_a(orco)
print(orco.esta_vivo())

¿Qué imprime cada print?

30 False

Las cuentas

50 − 20 = 30 · después 10 · después −10.
Con vida negativa, esta_vivo() devuelve False. 💀

Las cuatro herramientas, en un solo programa

🧬 Herenciaclass Jugador(Personaje) — el jugador es un personaje.
🧬 super()super().__init__(nombre, vida) — el padre hace su parte.
📦 returnesta_vivo() y atacar() — devuelven un valor que el programa usa.
🎁 Composiciónself.arma = arma — el jugador tiene un arma.

Ninguna es difícil sola.
Juntas, ya podés modelar casi cualquier cosa.

Gran desafío final · en parejas
👾

Falta el Enemigo

El orco de recién no ataca: solo recibe golpes. Queremos que se defienda.

La consigna

Escribí la clase Enemigo. Tiene que poder atacar igual que el jugador, pero con un Hechizo en vez de un arma… y además tener una dificultad.

Se puede usarTodo lo de hoy. Nada más.
PrimeroDibujarlo. El código recién después.
OjoNo escribas de nuevo nombre ni vida.

Las preguntas que hay que hacerse siempre

1¿Qué objetos hay acá?
2¿Qué debería tener la clase de arriba?
3¿Qué agrega cada hijo?
4¿Dónde usamos super()?
5¿Qué objeto tiene a otro objeto adentro?
6¿Qué método debería devolver un valor?
7¿Dónde estoy usando herencia y dónde composición?
Recién ahora, la solución
class Hechizo:
    def __init__(self, nombre, daño):
        self.nombre = nombre
        self.daño = daño

    def atacar(self):
        return self.daño

class Enemigo(Personaje):
    def __init__(self, nombre, vida, hechizo, dificultad):
        super().__init__(nombre, vida)
        self.hechizo = hechizo
        self.dificultad = dificultad

    def atacar_a(self, otro):
        daño = self.hechizo.atacar()
        otro.recibir_daño(daño)
        return daño

🧬 Hereda nombre y vida.
🧬 super() para no repetirlos.
🎁 Tiene un hechizo adentro.
📦 atacar() devuelve el daño.

Última pregunta difícil:
¿no convendría mover el arma a Personaje?

Lo que hay que llevarse de hoy

ES UN

↓

🧬 HERENCIA

TIENE UN

↓

🎁 COMPOSICIÓN

HACE ALGO Y ME LO MUESTRA

↓

print()

HACE ALGO Y ME ENTREGA EL RESULTADO

↓

return

Tres errores que van a cometer (y está bien)

🧬

Olvidarse el super()

Síntoma: AttributeError: has no attribute… El padre nunca hizo su parte.

📦

Usar print donde iba return

Síntoma: aparece un None donde esperabas un número.

🎁

Heredar cuando había que componer

Síntoma: la frase “es un” suena rara. un jugador es un arma

Tarea · Parte 1 de 3

Mirá alrededor y clasificá 👀

Encontrá 3 relaciones ES UN y 3 relaciones TIENE UN en cosas de tu vida real.

Ejemplos para arrancar

🧬 Un colectivo es un vehículo.
🎁 Un colectivo tiene un motor.

La prueba

Leelo en voz alta con las dos frases. Si una suena rara, esa no es.

Tarea · Parte 2 de 3

☕ Volvé a la cafetería del principio

Diseñala sin escribir código, usando las 7 preguntas.

Tiene que aparecer

🧬 Una herencia (algo que ES UN algo)
🎁 Una composición (algo que TIENE UN algo)
📦 Un método que devuelva un valor

Entregá

Un dibujo con las cajas y las flechas, como los de hoy. Nada de Python todavía.

Tarea · Parte 3 de 3

💻 Y ahora sí, a la compu

1 · Persona con nombre y edad.

2 · Estudiante hereda de Persona y agrega curso, usando super().

3 · Una clase Mochila con un método que devuelva el peso total.

4 · Que el Estudiante tenga una Mochila.

Para probar que anda

mochila = Mochila(3)
ana = Estudiante("Ana", 16, "5°B", mochila)

print(ana.nombre)
print(ana.mochila.peso_total())

★ Bonus · que el enemigo devuelva el golpe

No aprendimos comandos nuevos porque sí.

Aprendimos herramientas para representar mejor nuestras ideas.

La clase pasada aprendimos a ver objetos.
Hoy aprendimos a conectarlos.

Y para terminar

Si mañana te doy un problema nuevo… ¿qué es lo primero que deberías preguntarte?

“¿Qué objetos hay acá?”

Nos vemos la próxima 👋

Bloque 1
00:00 ← → avanzar · N notas · O índice · L bloquear clic 1 / 56
Notas del profesor

Recorrido de la clase

Clic para saltar · Esc para cerrar◇ = opcional, se puede saltear si vas corto