Esta guía abarca programación orientada a objetos (POO) en Python y administradores de contexto, incluyendo ejemplos prácticos de captura de stdout/stderr y uso del patrón Tee para mostrar y capturar salida al mismo tiempo.
1. Clases en Python
Las clases son un mecanismo clave de POO. Una clase es un molde para crear objetos con atributos (datos) y métodos (funciones). Los objetos creados desde una clase se llaman instancias. Las clases ayudan a estructurar el código, mejorar su reutilización y simplificar el mantenimiento.
Estructura de una clase
class ClassName:
def __init__(self, param1, param2):
self.param1 = param1
self.param2 = param2
def method(self):
return f'{self.param1} y {self.param2}'
Puntos clave:
__init__: constructor para inicializar atributos del objeto.self: referencia a la instancia actual.- Atributos: definidos mediante
self, accesibles en todos los métodos. - Métodos: operan sobre los datos del objeto.
Creación de objetos
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def description(self):
return f'{self.year} {self.make} {self.model}'
my_car = Car('Toyota', 'Corolla', 2020)
print(my_car.description()) # Salida: 2020 Toyota Corolla
Tipos de métodos
- Métodos de instancia — trabajan sobre objetos (
self). - Métodos de clase — trabajan sobre la clase (
cls) mediante@classmethod. - Métodos estáticos — independientes de instancia y clase (
@staticmethod).
Herencia
class Animal:
def speak(self):
return 'Sonido de animal'
class Dog(Animal):
def speak(self):
return 'Guau'
Polimorfismo
class Cat(Animal):
def speak(self):
return 'Miau'
cat = Cat()
print(cat.speak()) # Salida: Miau
- El mismo método funciona de manera diferente según el tipo de objeto.
Encapsulación
class Car:
def __init__(self, make, model):
self._make = make # Atributo protegido
self._model = model
def get_make(self):
return self._make
def set_make(self, make):
self._make = make
Métodos mágicos y destructor
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point({self.x}, {self.y})'
p = Point(3, 4)
print(p) # Salida: Point(3, 4)
Diagrama ASCII: Herencia y Polimorfismo
┌─────────┐
│ Animal │
└─────────┘
│ speak() │
└─────────┘
▲
┌───────┴────────┐
│ │
┌─────────┐ ┌─────────┐
│ Dog │ │ Cat │
└─────────┘ └─────────┘
│ speak() │ │ speak() │
└─────────┘ └─────────┘
2. Administradores de Contexto en Python
Los administradores de contexto permiten asignar y liberar recursos automáticamente, asegurando limpieza incluso ante errores.
Uso de @contextmanager
from contextlib import contextmanager
@contextmanager
def my_context():
print("Entrando en el contexto")
try:
yield "Recurso"
finally:
print("Saliendo del contexto")
Uso:
with my_context() as res:
print("Dentro del contexto:", res)
Captura de stdout/stderr y Tee
Python permite capturar stdout, stderr o ambos, y mostrar y capturar simultáneamente usando el patrón Tee.
3. Ejemplo completo: oop_and_context.py
"""
oop_and_context.py
Ejemplos de clases, principios de POO y administradores de contexto en Python.
Incluye:
- Definición de clases, herencia, polimorfismo, encapsulación
- Métodos de instancia, clase y estáticos
- Métodos mágicos y destructor
- Administradores de contexto con @contextmanager
- Captura de stdout, stderr y combinación de ambos
- Patrón Tee: mostrar y capturar simultáneamente
- Administrador de contexto basado en clase
"""
import sys
import io
from contextlib import contextmanager
# ==========================
# Parte 1: Clases y POO
# ==========================
class Animal:
"""Clase base para animales."""
def speak(self):
return 'Sonido de animal'
class Dog(Animal):
def speak(self):
return 'Guau'
class Cat(Animal):
def speak(self):
return 'Miau'
class Car:
"""Ejemplo de clase con encapsulación."""
def __init__(self, make, model, year=None):
self._make = make
self._model = model
self.year = year
def get_make(self):
return self._make
def set_make(self, make):
self._make = make
def description(self):
if self.year:
return f'{self.year} {self._make} {self._model}'
return f'{self._make} {self._model}'
class Point:
"""Clase con métodos mágicos."""
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point({self.x}, {self.y})'
# ==========================
# Parte 2: Administradores de contexto
# ==========================
@contextmanager
def my_context():
print("Entrando en el contexto")
try:
yield "Recurso"
finally:
print("Saliendo del contexto")
@contextmanager
def capture_stdout():
old_stdout = sys.stdout
sys.stdout = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stdout = old_stdout
@contextmanager
def capture_stderr():
old_stderr = sys.stderr
sys.stderr = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stderr = old_stderr
@contextmanager
def capture_output():
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = sys.stderr = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
class Tee(io.StringIO):
"""Salida visible y captura simultánea."""
def __init__(self, original):
super().__init__()
self.original = original
def write(self, text):
self.original.write(text)
super().write(text)
def flush(self):
self.original.flush()
@contextmanager
def capture_stdout_tee():
old_stdout = sys.stdout
sys.stdout = tee = Tee(old_stdout)
try:
yield tee
finally:
sys.stdout = old_stdout
# ==========================
# Parte 3: Administrador de contexto basado en clase
# ==========================
class OutputCapture:
"""Administrador de contexto basado en clase para capturar stdout."""
def __init__(self):
self._buffer = io.StringIO()
self._old_stdout = None
def __enter__(self):
self._old_stdout = sys.stdout
sys.stdout = self._buffer
return self._buffer
def __exit__(self, exc_type, exc_val, exc_tb):
sys.stdout = self._old_stdout
return False # propaga excepciones
# ==========================
# Parte 4: Demostraciones
# ==========================
def demo_classes():
print("\n--- Demostración de Clases ---")
dog = Dog()
cat = Cat()
print("Perro dice:", dog.speak())
print("Gato dice:", cat.speak())
my_car = Car('Toyota', 'Corolla', 2020)
print("Descripción del coche:", my_car.description())
my_car.set_make('Honda')
print("Marca actualizada:", my_car.get_make())
point = Point(3, 4)
print("Punto:", point)
def demo_context_managers():
print("\n--- Demostración de Administradores de Contexto ---")
with my_context() as res:
print("Dentro del contexto:", res)
with capture_stdout() as out:
print("stdout capturado")
print("Contenido:", out.getvalue().strip())
with capture_stderr() as err:
print("Esto va a stdout")
sys.stderr.write("Mensaje de error\n")
print("Errores:", err.getvalue().strip())
with capture_output() as out_all:
print("Stdout dentro de captura combinada")
sys.stderr.write("Stderr dentro de captura combinada\n")
print("Salida combinada:", out_all.getvalue().strip())
with capture_stdout_tee() as tee_out:
print("Salida visible Y capturada con Tee")
print("Buffer Tee:", tee_out.getvalue().strip())
with OutputCapture() as class_capture:
print("Captura usando administrador basado en clase")
print