Saltar al contenido principal

6.1 Clases y Objetos

En Python, una clase es una plantilla que define cómo serán los objetos que se creen a partir de ella.
Dentro de una clase se especifican los atributos (datos que guarda el objeto) y los métodos (acciones o comportamientos que puede realizar).

La idea es parecida a un plano de una casa:

  • La clase sería el plano (define cómo son las casas en general).
  • Cada objeto sería una casa concreta construida a partir de ese plano.

1️⃣ Definir clases y crear objetos​

🟩 Sintaxis básica para definir una clase​

La sintaxis general para crear una clase en Python es:

✍️ Sintaxis general de una clase
class NombreClase:
def __init__(self, parametros):
# Atributos de instancia (datos del objeto)
self.atributo1 = valor_inicial1
self.atributo2 = valor_inicial2

def metodo_ejemplo(self, otros_parametros):
# Comportamiento del objeto
# (instrucciones que se ejecutan al llamar al método)
...
en este ejemplo
  • class → palabra clave para indicar que se esta definiendo una clase.
  • NombreClase → nombre de la clase (por convención, empieza por mayúscula y se escribe en CamelCase).
  • __init__ → método especial llamado constructor. Se ejecuta automáticamente al crear el objeto.
  • self → referencia al propio objeto. Permite acceder a sus atributos y métodos.
  • método_ejemplo → método normal, que define una acción que los objetos pueden realizar.
Ejemplo de definición de una clase
class Persona:
def __init__(self, nombre, edad):
self.nombre = nombre # atributo de instancia
self.edad = edad # atributo de instancia

def saludar(self):
print(f"Hola, soy {self.nombre} y tengo {self.edad} años.")
en este ejemplo
  • La clase Persona define que todas las personas tendran un nombre y una edad.
  • El método saludar() describe un comportamiento común a todas las personas.

🟦 Objetos: instancias de una clase​

Un objeto es una instancia concreta de una clase. Cada objeto tiene sus propios valores para los atributos, aunque comparta los mismos métodos definidos en la clase.

Creación de objetos (instancias)
p1 = Persona("Ana", 25)
p2 = Persona("Luis", 30)

print(p1.nombre) # Ana
print(p2.edad) # 30

p1.saludar() # Hola, soy Ana y tengo 25 años.
p2.saludar() # Hola, soy Luis y tengo 30 años.
en este ejemplo
  • p1 y p2 son objetos de la clase Persona.
  • Ambos tienen los mismos atributos (nombre, edad) pero con valores distintos.
  • Los dos pueden usar el método saludar(), que se comporta de forma personalizada según los datos de cada objeto.

2️⃣ El constructor y el parámetro self​

En muchas clases es necesario que los objetos nazcan con unos datos mínimos: nombre, edad, saldo, color, etc.
Para ello se utiliza un método especial llamado constructor, que en Python se implementa con __init__().

🟩 El constructor __init__​

El método __init__ se ejecuta automáticamente cada vez que se crea un objeto de la clase. Sirve para inicializar los atributos del nuevo objeto. No devuelve nada (Python asume que el resultado es None).

✍️ Sintaxis general del constructor
class NombreClase:
def __init__(self, parametros):
# inicialización de atributos
self.atributo1 = valor1
self.atributo2 = valor2

Siempre que se escriba NombreClase(...), Python creará un nuevo objeto y llamará a __init__ para configurarlo.

🟦 El parámetro self​

El primer parámetro de __init__ (y de cualquier método de instancia) se llama por convención self y representa al propio objeto.

  • self permite acceder a los datos y métodos del objeto.
  • No hace falta pasar self al llamar al método: Python lo hace de forma automática.
Ejemplo del método __init__
class Coche:
def __init__(self, marca, color):
self.marca = marca # atributo del objeto
self.color = color # atributo del objeto

auto = Coche("Toyota", "rojo")
print(auto.marca) # Toyota
print(auto.color) # rojo
info

Aunque el nombre self no es obligatorio (podría llamarse este u objeto), es una convención estricta en Python y se recomienda usarlo siempre.

🟨 Constructores con valores por defecto​

Es posible definir parámetros con valores por defecto en __init__. Esto permite crear objetos con más o menos información según convenga.

Constructor con valores por defecto
class Persona:
def __init__(self, nombre, edad=18):
self.nombre = nombre
self.edad = edad

p1 = Persona("Ana", 25)
p2 = Persona("Luis") # edad tomará el valor por defecto: 18

print(p1.edad) # 25
print(p2.edad) # 18

🟧 Errores frecuentes con self​

no cometas estos errores
  1. Olvidar el parámetro self en la definición:

    class Persona:
    def __init__(nombre, edad): # ❌ Falta self
    self.nombre = nombre # NameError

    Aquí Python interpretara que nombre es self, y self no estará definido.

  2. No usar self al asignar atributos:

    class Persona:
    def __init__(self, nombre):
    nombre = nombre # ❌ no se guarda en el objeto

    Esta instrucción solo asigna la variable local nombre a si misma. Lo correcto es:

    self.nombre = nombre     # ✅ atributo del objeto

3️⃣ Atributos de clase y de instancia​

En una clase de Python se pueden definir dos tipos principales de atributos:

Tipo de atributoQué representaDónde se defineEjemplo
De instanciaPropio de cada objeto concretoDentro del método __init__self.nombre
De claseComún a todos los objetos de la claseDirectamente en la claseespecie = "Humano"

Los atributos de instancia guardan información particular de cada objeto (por ejemplo, el nombre de una persona).
Los atributos de clase representan un dato compartido por todas las instancias de esa clase (por ejemplo, que todas las personas son humanas).

Ejemplo de atributos de clase e instancia
class Persona:
especie = "Humano" # Atributo de clase (común a todas las personas)

def __init__(self, nombre):
self.nombre = nombre # Atributo de instancia (propio de cada objeto)

p1 = Persona("Ana")
p2 = Persona("Luis")

print(p1.especie) # Humano
print(p2.especie) # Humano
print(p1.nombre) # Ana
print(p2.nombre) # Luis

🟩 Cómo accede Python a los atributos​

Cuando se escribe p1.especie, Python busca el atributo en este orden:

  1. Primero en los atributos de instancia del objeto p1.
  2. Si no lo encuentra, lo busca en los atributos de la clase Persona.

Por eso es habitual acceder a los atributos de clase también a través de los objetos, aunque la forma recomendada para dejarlo más claro es usar el nombre de la clase:

Acceso recomendado a atributos de clase
print(Persona.especie)  # Mejor para dejar claro que es algo común a la clase

Es importante distinguir entre:

  • Cambiar el atributo de clase para todos los objetos.
  • Crear o modificar un atributo de instancia solo para un objeto concreto.
Diferencia entre cambiar la clase y la instancia
class Persona:
especie = "Humano"

def __init__(self, nombre):
self.nombre = nombre

p1 = Persona("Ana")
p2 = Persona("Luis")

# Cambiar el atributo de CLASE
Persona.especie = "Humano terrestre"

print(p1.especie) # Humano terrestre
print(p2.especie) # Humano terrestre

# Sobrescribir SOLO para p1 (crea un atributo de INSTANCIA llamado 'especie')
p1.especie = "Extraterrestre amistoso"

print(p1.especie) # Extraterrestre amistoso
print(p2.especie) # Humano terrestre
print(Persona.especie) # Humano terrestre

🟦 ¿Cuándo usar cada tipo de atributo?​

Atributos de instancia (dentro de __init__, usando self):

  • Datos que cambian de un objeto a otro.
  • Ejemplos: nombre, edad, saldo, color.

Atributos de clase (definidos directamente en la clase):

  • Información común a todas las instancias.
  • Valores constantes o configuraciones generales.
  • Contadores o estadísticas compartidas por todos los objetos.
Uso típico de un atributo de clase como contador
class Alumno:
total_alumnos = 0 # atributo de clase

def __init__(self, nombre):
self.nombre = nombre # atributo de instancia
Alumno.total_alumnos += 1 # se incrementa al crear cada objeto

a1 = Alumno("Ana")
a2 = Alumno("Luis")

print(Alumno.total_alumnos) # 2

4️⃣ Métodos de instancia​

Los métodos de instancia son funciones definidas dentro de una clase que describen las acciones que puede realizar un objeto.
Se llaman “de instancia” porque siempre trabajan con una instancia concreta (un objeto) y reciben como primer parámetro self, que representa a ese objeto.

En cierto modo:

  • Una función normal trabaja con los datos que se le pasan.
  • Un método trabaja con los datos que pertenecen a un objeto (self.atributo).

La forma general de definir un método de instancia es:

✍️Sintaxis general de un método de instancia
class NombreClase:
def nombre_metodo(self, otros_parametros):
# bloque de instrucciones
...
en este ejemplo
  • def → define el método.
  • self → referencia al propio objeto.
  • otros_parámetros → datos adicionales que el método necesita para trabajar.
Ejemplo de método de instancia
class Persona:
def __init__(self, nombre):
self.nombre = nombre

def saludar(self):
print(f"Hola, soy {self.nombre}")

p = Persona("Ana")
p.saludar()

5️⃣ Organización del código​

Las clases viven en “módulos” (archivos .py), y el main.py debería ser solo el punto de entrada que las usa, no donde vive toda la lógica.

Luego según el tamaño del proyecto, cambiamos la estrategia.

En cuanto el programa tenga pocas clases emplearemos:

  • Un archivo donde se ejecuta el programa (por ejemplo main.py).
  • Un archivo donde se definen las clases (módulos).
mi_proyecto/
main.py # punto de entrada, usa las clases
modelos.py # clases principales

Cuando el proyecto crece, lo habitual es:

  • Agrupar clases relacionadas en el mismo archivo (módulo).
  • No hace falta una clase por archivo, salvo que sean muy grandes o importantes.

Ejemplo:

mi_proyecto/
main.py
personas.py # Persona, Alumno, Profesor...
cursos.py # Curso, Asignatura...

✅ Buenas prácticas al definir clases​

  • Usa nombres en singular y con mayúscula inicial para las clases (Alumno, Coche, Factura).
  • Los nombres de métodos y atributos deben ir en minúsculas y con guiones bajos (saludar, nombre_cliente).
  • Evita incluir demasiada lógica en el constructor __init__.
Consejo

Antes de escribir código, piensa qué clases necesitas y qué datos o acciones deben tener. Este diseño previo te permitirá construir programas más ordenados y fáciles de mantener.


⚔️ Actividad Práctica: La Arena​

En esta actividad pondremos en práctica todo lo aprendido sobre clases, objetos, constructores y atributos mediante la creación de un simulador de combate RPG por niveles.

🏟️ 1. La arena de entrenamiento​

En un videojuego de rol, los personajes deben entrenar en una arena de combate antes de enfrentarse a enemigos reales. Cada personaje tiene unas características básicas como su nombre, la cantidad de vida que posee y la fuerza de sus ataques.

La academia de entrenamiento te ha encargado desarrollar un programa sencillo en Python, llamado personaje.py, que permita modelar a estos personajes y simular un combate básico entre dos de ellos.

Tu tarea consiste en definir una clase que represente a un personaje y utilizarla para crear dos combatientes que se ataquen durante varios turnos.

📋 Tarea a realizar​

Se debe crear una clase llamada Personaje que represente a un combatiente del juego. Cada personaje debe tener:

  • Un nombre.
  • Una cantidad de vida (no puede ser nunca negativa).
  • Un valor de ataque.

Además, la clase debe incluir métodos que permitan:

  • Comprobar si el personaje sigue con vida (esta_vivo).
  • Recibir daño cuando es atacado. Cuando un personaje recibe daño, existe una probabilidad de un 10% de que su armadura absorba el golpe, evitando que pierda vida.
  • Atacar a otro personaje. Cuando un personaje ataca a otro, el daño producido no será fijo, sino un valor aleatorio entre 0 y el ataque máximo del atacante.
  • Mostrar su estado actual (nombre, vida y ataque).

Una vez definida la clase, el programa deberá:

  1. Crear dos personajes distintos.
  2. Mostrar el estado inicial de ambos.
  3. Simular 3 turnos de combate, en los que los personajes se atacan de forma alterna.
  4. Mostrar el estado de los personajes tras cada ataque.
  5. Finalizar el combate antes de los 3 turnos si alguno de los personajes se queda sin vida.
Ejemplo de salida Fase 1
== COMBATE ==
Heroe | Vida: 30 | Ataque: 6
Goblin | Vida: 22 | Ataque: 4

-- Turno 1 --
Heroe ataca a Goblin y causa 5 de daño
Goblin | Vida: 17 | Ataque: 4

Goblin ataca a Heroe y causa 3 de daño
La armadura de Heroe ha absorbido el daño
Heroe | Vida: 30 | Ataque: 6

🎒 2. El equipo del aventurero​

En la arena de entrenamiento, los personajes no solo luchan con su fuerza, también dependen del equipo y los objetos que llevan consigo. Pociones, armas o amuletos pueden marcar la diferencia durante un combate.

La academia de entrenamiento quiere ampliar el sistema de combate desarrollado anteriormente para que los personajes puedan almacenar y gestionar objetos mediante un inventario.

Tu tarea consiste en crear nuevas clases que representen los objetos del juego y un inventario, y asociar dicho inventario a cada personaje. Desarrolla el archivo inventario.py.

📋 Tarea a realizar​

Se deben crear las siguientes clases:

  1. Clase Item: Representa un objeto genérico del juego. Cada objeto debe tener un nombre y un peso.
  2. Clase Inventario: Representa el inventario de un personaje. Debe incluir:
    • Una colección de objetos (Item).
    • Un método para añadir un objeto al inventario.
    • Un método para calcular el peso total del inventario.
    • Un método para mostrar el contenido del inventario de forma ordenada.
  3. Modificación de la clase Personaje:
    • Cada personaje tenga un inventario propio. El inventario se cree automáticamente al crear el personaje.

Una vez definidas las clases, el programa deberá crear un personaje, añadir al menos tres objetos distintos a su inventario y mostrar el estado completo (personaje + contenido del inventario + peso total).

Ejemplo de salida Fase 2
Heroe | Vida: 30 | Ataque: 6
Inventario:
1. Pocion pequeña (0.2 kg)
2. Espada vieja (2.5 kg)
3. Amuleto magico (0.1 kg)
Peso total del inventario: 2.8 kg

👿 3. El control de la arena​

La academia de entrenamiento necesita llevar un registro global de todos los enemigos que han sido creados para las sesiones de combate. Este control es importante para analizar la dificultad de la arena y ajustar el número de combates que realizan los aspirantes.

Para ello, se ha decidido ampliar el sistema de personajes incorporando un contador común que permita saber cuántos enemigos se han generado durante la ejecución del programa.

Tu tarea consiste en crear una clase que represente a los enemigos de la arena y mantener un registro del número total de enemigos creados en el archivo enemigo.py.

📋 Tarea a realizar​

Se debe crear una clase llamada Enemigo que represente a los adversarios del juego. Cada enemigo debe tener:

  • Un tipo o nombre identificativo.
  • Una cantidad de vida.
  • Un valor de ataque.

Además, la clase debe llevar un registro global (atributo de clase) del número total de enemigos creados desde que se inicia el programa.

El programa deberá crear varios enemigos de distintos tipos, aumentar automáticamente el contador global y finalmente mostrar el número total de enemigos creados junto al estado de cada uno.

Ejemplo de salida Fase 3
Enemigos creados en la arena: 5

1. Goblin | Vida: 20 | Ataque: 4
2. Lobo | Vida: 18 | Ataque: 5
3. Bandido | Vida: 24 | Ataque: 6
4. Esqueleto | Vida: 22 | Ataque: 5
5. Ogro | Vida: 35 | Ataque: 8

🏆 4. El desafío final​

Tras las sesiones de entrenamiento iniciales, la academia pone a prueba a los aspirantes en una arena real. En esta fase, el personaje del jugador deberá enfrentarse a varios enemigos utilizando todo lo desarrollado anteriormente: combate, inventario y control de enemigos.

Se debe crear un archivo principal llamado arena.py que utilice las clases desarrolladas en los bloques anteriores.

📋 Tarea a realizar​

El programa deberá seguir el siguiente flujo general:

  1. Crear un personaje principal que representará al jugador.
  2. Crear varios enemigos utilizando la clase Enemigo.
  3. Enfrentar al jugador contra los enemigos uno por uno, en el orden en que fueron creados.
  4. Durante cada combate:
    • El jugador y el enemigo se atacan de forma alterna con daño aleatorio.
    • Existe la probabilidad de que la armadura absorba el daño.
    • El combate termina cuando uno se queda sin vida.
    • Si el jugador vence, puede recibir un objeto como recompensa y pasa al siguiente.
    • Si el jugador pierde, la sesión de la arena finaliza.

Al finalizar, se deberá mostrar el número total de personajes enemigos generados (usando el atributo de clase) y el estado final del héroe.

Ejemplo de salida Fase 4
== INICIO DE LA ARENA ==
El aspirante Heroe entra en la arena preparado para el combate.
Objetos iniciales: Pocion (0.5 kg), Espada (2.0 kg)
Total de enemigos a batir: 3

-- COMBATE 1: Heroe vs Goblin --
Heroe | Vida: 30 | Ataque: 6
Goblin | Vida: 15 | Ataque: 3
...
¡Heroe ha vencido a Goblin!
¡Has obtenido un nuevo objeto: Escudo pequeño (1.5 kg)!

-- COMBATE 2: Heroe vs Lobo --
...
== FIN DEL ENTRENAMIENTO ==
Heroe ha completado todos los combates con 12 puntos de vida restantes.
Total de personajes enemigos generados: 3