1Definiciones¶
- Introspección
- Es la capacidad de un programa para examinar su propia estructura y estado en tiempo de ejecución.
- En Python, gracias a su naturaleza dinámica, podemos inspeccionar tipos, atributos y métodos de objetos, incluso sin conocerlos de antemano.
- Reflexión
- Va un paso más allá: no solo inspecciona, sino que modifica el comportamiento o la estructura de objetos, clases o módulos en tiempo de ejecución.
2Herramientas comunes de introspección y reflexión¶
| Función | Descripción |
|---|---|
type(obj) | Devuelve el tipo del objeto. |
dir(obj) | Lista atributos y métodos disponibles. |
id(obj) | Identificador único en memoria. |
vars(obj) | Diccionario de atributos de instancia. |
getattr(obj, name[, default]) | Obtiene un atributo dinámicamente. |
setattr(obj, name, value) | Asigna un atributo dinámicamente. |
hasattr(obj, name) | Verifica si un atributo existe. |
callable(obj) | Indica si el objeto es invocable como función o método. |
help(obj) | Muestra la documentación. |
3Ejemplo básico de introspección¶
class Persona:
"""Clase simple para ejemplo de introspección."""
def __init__(self, nombre, edad):
self.nombre = nombre
self.edad = edad
def saludar(self):
print(f"Hola, soy {self.nombre} y tengo {self.edad} años.")
persona = Persona("Alice", 30)3.1Inspección¶
print("Tipo de objeto:", type(persona))
print("Atributos de instancia:", vars(persona))
print("ID (memoria):", id(persona))
print("¿Tiene atributo 'nombre'?", hasattr(persona, "nombre"))
print("Nombre:", getattr(persona, "nombre"))Output
Tipo de objeto: <class '__main__.Persona'>
Atributos de instancia: {'nombre': 'Alice', 'edad': 30}
ID (memoria): 140456048795600
¿Tiene atributo 'nombre'? True
Nombre: Alice
3.2Modificación controlada¶
setattr(persona, "edad", 31)
print("Edad actualizada:", getattr(persona, "edad"))Output
Edad actualizada: 31
3.3Verificar invocabilidad¶
print("¿Es 'persona' invocable?", callable(persona))
print("¿Es 'persona.saludar' invocable?", callable(persona.saludar))Output
¿Es 'persona' invocable? False
¿Es 'persona.saludar' invocable? True
4Ejemplo de reflexión con cautela¶
En Python es posible agregar atributos o métodos a un objeto existente en tiempo de ejecución.
Esto otorga flexibilidad, pero puede volver el código difícil de mantener si se abusa.
import types
class Persona:
def __init__(self, nombre, edad):
self.nombre = nombre
self.edad = edad
def saludar(self):
print(f"Hola, soy {self.nombre} y tengo {self.edad} años.")
ana = Persona("Ana", 35)
juan = Persona("Juan", 40)
# Agregar atributos en tiempo de ejecución
ana.domicilio = "La Merced 123"
juan.telefono = "123-456-7890"
# Agregar un método personalizado solo a 'juan'
def saludar_con_telefono(self):
print(
f"Hola, soy {self.nombre}, tengo {self.edad} años "
f"y mi teléfono es {self.telefono}."
)
juan.saludar = types.MethodType(saludar_con_telefono, juan)
ana.saludar()
juan.saludar()Output
Hola, soy Ana y tengo 35 años.
Hola, soy Juan, tengo 40 años y mi teléfono es 123-456-7890.
5Uso de help() para documentación¶
help() es una función incorporada en Python que proporciona información sobre objetos, funciones y módulos. Es especialmente útil para obtener documentación sobre cómo usar un objeto o qué métodos y atributos tiene.
class Persona:
"""Clase simple para ejemplo de documentación."""
def __init__(self, nombre, edad):
self.nombre = nombre
self.edad = edad
def saludar(self):
"""Método para saludar."""
print(f"Hola, soy {self.nombre} y tengo {self.edad} años.")
# Crear instancia
alice = Persona("Alice", 30)
# Usar help()
help(alice)Output
Help on Persona in module __main__ object:
class Persona(builtins.object)
| Persona(nombre, edad)
|
| Clase simple para ejemplo de documentación.
|
| Methods defined here:
|
| __init__(self, nombre, edad)
| Initialize self. See help(type(self)) for accurate signature.
|
| saludar(self)
| Método para saludar.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables
|
| __weakref__
| list of weak references to the object
6Uso de eval() para evaluar expresiones¶
La función eval() permite ejecutar expresiones Python desde una cadena de texto. Esto puede ser útil para evaluar dinámicamente código, pero debe usarse con extrema precaución debido a implicaciones de seguridad.
Por ejemplo, el siguiente código es peligroso si la variable expresion proviene de un usuario no confiable:
expresion = input("Introduce una expresión: ")
# ¡Peligroso! El usuario puede ejecutar cualquier código Python.
resultado = eval(expresion)
print("Resultado de la expresión:", resultado)x = 10
expresion = "x * 2"
resultado = eval(expresion)
print("Resultado de la expresión:", resultado)Output
Resultado de la expresión: 20
7Buenas Prácticas¶
La introspección y reflexión son herramientas poderosas pero que se deben utilizar con responsabilidad para construir código flexible y no código frágil. Algunas recomendaciones son:
Usar introspección para depuración
No usar introspección y reflexión como sustituto de un buen diseño
Evitar abusar de la reflexión para modificar clases/objetos, ya que reduce la previsibilidad del código.
Documentar cambios dinámicos para facilitar mantenimiento.
Si se requiere reflexión, documentar claramente y mantener el cambio localizado.
En proyectos grandes, preferir patrones de diseño que hagan explícitas las extensiones (por ejemplo, decoradores).
Preferir alternativas explícitas (herencia, interfaces, polimorfismo) cuando sea posible.