Cargando tu progreso…

Pokédex Codex

Python: POO Introducción

META

🎯 Meta: aprender Programación Orientada a Objetos (POO). Vas a crear tus propios “moldes” para fabricar Pokémon, cada uno con sus datos y sus acciones.


🎮 Analogía: el molde y los Pokémon

Imaginá la especie “Pikachu” como un molde: define que todo Pikachu tiene un nombre, un nivel, HP, y que puede usar Impactrueno. Pero TU Pikachu y el Pikachu de Ash son individuos distintos: mismo molde, datos diferentes.

En POO:

  • El molde se llama clase (la especie, el plano).
  • Cada individuo fabricado con ese molde se llama objeto o instancia (tu Pikachu concreto).

🏗️ Definir una clase

Una clase se define con la palabra class. Por convención, su nombre empieza con mayúscula.

class Pokemon:
    pass    # por ahora vacía

Para crear un objeto (instancia) de esa clase, la llamás como si fuera una función:

mi_pokemon = Pokemon()    # fabricamos un Pokémon con el molde

🧬 init: el constructor

El método __init__ es el constructor: se ejecuta automáticamente cuando creás un objeto. Sirve para darle sus datos iniciales.

class Pokemon:
    def __init__(self, nombre, tipo, nivel):
        # 'self' es el objeto que se está creando.
        # Guardamos los datos DENTRO del objeto con self.
        self.nombre = nombre
        self.tipo = tipo
        self.nivel = nivel

# Al crear el objeto, pasamos los datos que pide __init__.
pikachu = Pokemon("Pikachu", "Electrico", 25)
print(pikachu.nombre)    # Pikachu
print(pikachu.nivel)     # 25

¿Qué es self?

self es el propio objeto. Es la forma en que el objeto se refiere a sí mismo. Cuando hacés self.nombre = nombre, estás diciendo “guardá este nombre adentro mío”.

TIP

💡 self siempre es el primer parámetro de los métodos, pero no lo pasás al llamarlos: Python lo hace solo. Es automático.


P: En `def __init__(self, nombre, nivel)`, ¿para qué sirve `self`?
- Es el nombre de la clase
- Es el primer argumento que pasás al crear el objeto
+ Es el propio objeto: sirve para guardar datos adentro con `self.algo`
> `self` siempre es el primer parámetro de los métodos y representa al objeto. Python lo pasa solo; vos no lo escribís cuando llamás el método.

📊 Atributos: los datos del objeto

Los atributos son las variables que viven dentro de un objeto (self.nombre, self.nivel…). Son sus “stats”.

pikachu = Pokemon("Pikachu", "Electrico", 25)

print(pikachu.nivel)      # leer un atributo: 25
pikachu.nivel = 26        # modificar un atributo
print(pikachu.nivel)      # 26

Cada objeto tiene sus propios atributos, independientes de los demás:

pikachu = Pokemon("Pikachu", "Electrico", 25)
charizard = Pokemon("Charizard", "Fuego", 50)

print(pikachu.nivel)      # 25
print(charizard.nivel)    # 50  (son distintos)

⚡ Métodos: las acciones del objeto

Un método es una función definida dentro de la clase. Son las “acciones” o “ataques” que el objeto puede hacer. Siempre reciben self primero.

class Pokemon:
    def __init__(self, nombre, tipo, nivel):
        self.nombre = nombre
        self.tipo = tipo
        self.nivel = nivel
        self.hp = 100

    def atacar(self):
        # Un método puede usar los atributos del objeto con self.
        return f"{self.nombre} ataca con un golpe de tipo {self.tipo}!"

    def subir_nivel(self):
        # Un método puede modificar los atributos del objeto.
        self.nivel = self.nivel + 1

pikachu = Pokemon("Pikachu", "Electrico", 25)
print(pikachu.atacar())     # Pikachu ataca con un golpe de tipo Electrico!
pikachu.subir_nivel()
print(pikachu.nivel)        # 26
TIP

💡 Fijate: llamás los métodos con objeto.metodo(). No pasás self, Python lo manda solo.


🖨️ str: cómo se muestra el objeto

Por defecto, si hacés print(pikachu), Python muestra algo feo como <__main__.Pokemon object at 0x7f...>. El método especial __str__ define un texto lindo.

class Pokemon:
    def __init__(self, nombre, nivel):
        self.nombre = nombre
        self.nivel = nivel

    def __str__(self):
        # Lo que devuelva esto es lo que ve el usuario con print().
        return f"{self.nombre} (Nivel {self.nivel})"

pikachu = Pokemon("Pikachu", 25)
print(pikachu)    # Pikachu (Nivel 25)  ← ¡mucho mejor!

🔍 repr: la versión para programadores

__repr__ es parecido a __str__, pero es la representación “técnica”, la que ves en el REPL o cuando un objeto está dentro de una lista. La idea es que sea precisa y útil para debuggear.

class Pokemon:
    def __init__(self, nombre, nivel):
        self.nombre = nombre
        self.nivel = nivel

    def __repr__(self):
        return f"Pokemon(nombre='{self.nombre}', nivel={self.nivel})"

pikachu = Pokemon("Pikachu", 25)
print(repr(pikachu))    # Pokemon(nombre='Pikachu', nivel=25)
equipo = [pikachu]
print(equipo)           # [Pokemon(nombre='Pikachu', nivel=25)]
TIP

💡 Regla práctica: __str__ para el usuario (lindo), __repr__ para vos (preciso). Si solo vas a definir uno, definí __repr__.


P: Tenés `pikachu = Pokemon("Pikachu", 25)` y `charizard = Pokemon("Charizard", 50)`. Si hacés `pikachu.nivel = 26`, ¿cambia el nivel de Charizard?
+ No, cada objeto tiene sus propios atributos
- Sí, todos los objetos de la misma clase comparten atributos
- Depende si la clase usa `self` o no
> Cada instancia (objeto) tiene su propio espacio de datos. Cambiar `pikachu.nivel` no afecta a `charizard` ni a ningún otro objeto.

🔒 Encapsulamiento básico

Encapsular es proteger los datos internos de un objeto. En Python, por convención, un atributo “privado” (que no debería tocarse desde afuera) se nombra con un guión bajo adelante.

class Pokemon:
    def __init__(self, nombre):
        self.nombre = nombre
        self._hp = 100         # _hp: "esto es interno, no lo toques directo"

    def recibir_dano(self, cantidad):
        # La forma CORRECTA de cambiar el HP es a través de un método,
        # que puede validar (que no baje de 0, por ejemplo).
        self._hp = self._hp - cantidad
        if self._hp < 0:
            self._hp = 0

    def hp_actual(self):
        return self._hp

El guión bajo no lo “bloquea” de verdad (Python confía en vos), pero es una señal clara: “accedé a esto solo a través de los métodos”. Más adelante lo profundizamos con @property (en POO: Avanzado).


📝 Resumen

class Pokemon:                          # clase = molde
    def __init__(self, nombre, nivel):  # constructor
        self.nombre = nombre            # atributos (datos del objeto)
        self.nivel = nivel
        self._hp = 100                  # _hp: atributo "interno"

    def atacar(self):                   # método (acción)
        return f"{self.nombre} ataca!"

    def __str__(self):                  # texto lindo para print()
        return f"{self.nombre} (Nv {self.nivel})"

    def __repr__(self):                 # texto técnico para debug
        return f"Pokemon({self.nombre!r}, {self.nivel})"

pikachu = Pokemon("Pikachu", 25)        # instancia (objeto)
print(pikachu.atacar())
print(pikachu)
ConceptoQué es
classEl molde / plano
objeto / instanciaUn individuo hecho con el molde
__init__El constructor (datos iniciales)
selfEl propio objeto
atributoUn dato del objeto (self.x)
métodoUna acción del objeto
__str__Texto lindo para el usuario
__repr__Texto técnico para debug
_atributoConvención de “interno/privado”

➡️ ¿Y ahora qué?

Ahora practicá: andá a los ejercicios de este tema y resolvelos. Se corrigen al instante con tests reales en tu navegador. 💪

ÁNIMO

“Una clase es un molde. Con un buen molde, fabricás Pokémon ilimitados.”