Clase 2: Introducción a OOP¶
En esta clase, se introduce el tema de programación orientada a objetos (OOP por sus siglas en inglés).
¿Qué es la programación orientada a objetos?¶
La programación orientada a objetos corresponde a un paradigma de programación; es decir, una forma de escribir código que organiza la lógica en objetos.
Un objeto:
- Representa algo del mundo real o un concepto.
- Tiene atributos (datos que describen su estado).
- Tiene métodos (acciones que puede realizar).
Ejemplo
Un objeto Perro
podría tener atributos como nombre
y edad
, y métodos como ladrar()
o correr()
.
¿Por qué usar OOP?¶
La OOP sirve para:
- Modelar problemas de forma más cercana a la realidad.
- Organizar el código en bloques reutilizables.
- Facilitar el mantenimiento y la expansión del programa.
Conceptos clave¶
Concepto | Definición |
---|---|
Clase | Plantilla o molde que define cómo serán los objetos (qué datos tendrán y qué podrán hacer). |
Objeto | Una instancia de una clase. Cada objeto tiene sus propios valores de atributos. |
Atributo | Variable que almacena datos dentro de un objeto. |
Método | Función que pertenece a un objeto y que puede usar o modificar sus atributos. |
__init__ |
Método especial de inicialización; define y da valor inicial a atributos |
self |
Referencia a esta instancia; permite acceder a su estado |
Errores frecuentes
- Olvidar
self
como primer parámetro en métodos de instancia. - Declarar atributos fuera de
__init__
y pretender que existan en todas las instancias. - Tratar la clase como si fuera un objeto (p. ej., usar
Persona.nombre
esperando el nombre de cada persona).
Diagrama conceptual del ejemplo a realizar¶
classDiagram
class Persona {
- nombre: str
- edad: int
+ saludar(): void
}
sequenceDiagram
participant Main
participant Clase as Clase Persona
participant p1 as Objeto p1
participant p2 as Objeto p2
Main->>Clase: crear("Ana", 21)
activate Clase
Clase-->>Main: retorna p1
deactivate Clase
Main->>Clase: crear("Luis", 22)
activate Clase
Clase-->>Main: retorna p2
deactivate Clase
Main->>p1: p1.saludar()
Main->>p2: p2.saludar()
Ejemplo mínimo en Python¶
class Persona:
def __init__(self, nombre, edad): # (1)!
self.nombre = nombre # (2)!
self.edad = edad
def saludar(self): # (3)!
print(f"Hola, soy {self.nombre} y tengo {self.edad} años.") # (4)
# Crear (instanciar) objetos
p1 = Persona("Ana", 21) # (5)!
p2 = Persona("Luis", 22)
# Usar métodos de instancia
p1.saludar()
p2.saludar() # (6)!
__init__
se ejecuta al crear cada objeto; sirve para inicializar atributos.self.nombre
yself.edad
son atributos de instancia; pertenecen a esta persona.saludar
es un método de instancia: siempre defineself
como primer parámetro.- Dentro del método,
self
permite acceder al estado de esta instancia. p1
es un objeto connombre="Ana"
,edad=21
.- La llamada
obj.metodo()
ejecuta el comportamiento en ese objeto.
Estado independiente por objeto¶
class Contador:
def __init__(self, valor_inicial=0):
self.valor = valor_inicial
c1 = Contador()
c2 = Contador(10)
c1.valor += 5
c2.valor += 1
print(c1.valor) # (1)!
print(c2.valor) # (2)!
print(id(c1) != id(c2)) # (3)!
- 5
- 11
True
: objetos distintos
Concepto clave
Aunque c1
y c2
provienen de la misma clase, cada objeto almacena su propio estado.
Cambiar c1.valor
no afecta c2.valor
, y viceversa.
Métodos de instancia: consultar y mutar¶
Los métodos se suelen dividir en estos dos tipos por funcoinalidad.
Buenas prácticas con métodos
- Un método, una responsabilidad clara.
- Nombres descriptivos (verbos para acciones).
- Evitar efectos secundarios en métodos que aparentan consultar.
Ejemplo de métodos de instancia¶
class Producto:
def __init__(self, nombre: str, precio: float, stock: int = 0):
self.nombre = nombre
self.precio = precio
self.stock = stock
def disponible(self) -> bool:
return self.stock > 0
def reponer(self, cantidad: int) -> None:
self.stock += cantidad
def vender(self, cantidad: int) -> bool:
if cantidad <= self.stock:
self.stock -= cantidad
return True
return False
a = Producto("Cuaderno", 900.0, 5)
b = Producto("Bolígrafo", 350.0)
print(a.disponible(), b.disponible()) # (1)!
b.reponer(10)
print(b.disponible()) # (2)!
True
,False
True
Preguntas rápidas
- ¿Qué rol cumple
__init__
en la creación de objetos? - ¿Cómo accede un método al estado de su propia instancia?
- ¿Qué diferencia hay entre un método que consulta y uno que muta?
Errores típicos y cómo evitarlos¶
Mejor: crear self.x
en __init__
, y luego modificarlo en métodos.
Ejercicios guiados¶
Ejercicio 1¶
Cree una clase Libro
con atributos de instancia titulo
(str) y paginas
(int).
Añada:
descripcion()
: consultor que retorna"<titulo> (<paginas> págs.)"
.agregar_paginas(n)
: mutador que suman
sin > 0
.
Instancie dos libros y demuestre que sus estados son independientes.
class Libro:
def __init__(self, titulo: str, paginas: int):
self.titulo = titulo
self.paginas = paginas
def descripcion(self) -> str: # consultor
return f"{self.titulo} ({self.paginas} págs.)"
def agregar_paginas(self, n: int) -> None: # mutador
if n > 0:
self.paginas += n
l1 = Libro("POO en Python", 180)
l2 = Libro("Algoritmos", 250)
l1.agregar_paginas(20)
print(l1.descripcion()) # (1)!
print(l2.descripcion()) # (2)!
- POO en Python (200 págs.)
- Algoritmos (250 págs.)
Ejercicio 2¶
Cree Termometro
con atributo celsius
(float).
Añada:
a_fahrenheit()
: consultor que retorna la conversión.ajustar(delta)
: mutador que sumadelta
al valor en °C. Instancie dos termómetros y evidencie que mutar uno no afecta el otro.
class Termometro:
def __init__(self, celsius: float):
self.celsius = celsius
def a_fahrenheit(self) -> float: # consultor
return self.celsius * 9/5 + 32
def ajustar(self, delta: float) -> None: # mutador
self.celsius += delta
t1 = Termometro(20.0)
t2 = Termometro(30.0)
t1.ajustar(5.0)
print(t1.celsius, t1.a_fahrenheit()) # (1)!
print(t2.celsius, t2.a_fahrenheit()) # (2)!
25.0 77.0
30.0 86.0