Introducción
Contenido de la lección.
Objetivos de aprendizaje
- Objetivo 1
- Objetivo 2
- Objetivo 3
Objetivos de Aprendizaje
Al finalizar este tema, serás capaz de:
- Comprender el concepto de alcance de variables y espacios de nombres en Python
- Distinguir entre variables locales, globales y no locales, aplicando las reglas LEGB
- Implementar funciones que manejen correctamente el scope para evitar efectos secundarios
- Aplicar buenas prácticas en el manejo de variables en proyectos de software profesional
📚 Conceptos Fundamentales: Espacios de Nombres
Un espacio de nombres (namespace) es un contenedor que mantiene una relación entre nombres de variables y sus valores correspondientes. Python utiliza diferentes espacios de nombres para organizar el código: el espacio built-in (funciones predefinidas como print(), len()), el espacio global (nivel de módulo), y espacios locales (dentro de funciones). Cada espacio de nombres tiene un ciclo de vida específico: el built-in existe durante toda la ejecución del programa, el global existe desde la importación del módulo hasta el final del programa, y los locales se crean y destruyen con cada llamada a función. La regla LEGB (Local, Enclosing, Global, Built-in) define el orden en que Python busca variables: primero en el ámbito local actual, luego en funciones enclosing (anidadas), después en el ámbito global, y finalmente en el built-in.
# Variable global
variable_global = 'Soy global'
def funcion_externa():
# Variable local de función externa
variable_local_externa = 'Soy local externa'
def funcion_interna():
# Variable local de función interna
variable_local_interna = 'Soy local interna'
print('Desde interna:', variable_local_interna)
print('Accediendo a externa:', variable_local_externa)
print('Accediendo a global:', variable_global)
funcion_interna()
print('Desde externa:', variable_local_externa)
funcion_externa()
print('Desde global:', variable_global)
Desde interna: Soy local interna Accediendo a externa: Soy local externa Accediendo a global: Soy global Desde externa: Soy local externa Desde global: Soy global
🎯 Variables Locales y Globales
Las variables locales son aquellas definidas dentro de una función y solo son accesibles dentro de esa función. Se crean cuando la función se ejecuta y se destruyen cuando termina. Las variables globales se definen en el nivel del módulo y son accesibles desde cualquier parte del programa. Para modificar una variable global desde dentro de una función, se debe usar la palabra clave 'global'. Sin esta declaración, Python creará una nueva variable local con el mismo nombre, lo que puede causar errores sutiles. Es importante entender que la asignación dentro de una función siempre crea una variable local a menos que se declare explícitamente como global. La lectura de variables sigue la regla LEGB, pero la escritura requiere consideraciones especiales.
contador = 0 # Variable global
def incrementar_local():
contador = 1 # Variable local (no modifica la global)
print(f'Local: {contador}')
def incrementar_global():
global contador # Declaramos que usaremos la global
contador += 1
print(f'Global: {contador}')
print(f'Inicial: {contador}')
incrementar_local()
print(f'Después de local: {contador}')
incrementar_global()
print(f'Después de global: {contador}')
Inicial: 0 Local: 1 Después de local: 0 Global: 1 Después de global: 1
💡 Funciones Anidadas y Nonlocal
Las funciones anidadas (funciones definidas dentro de otras funciones) introducen un nivel adicional de complejidad en el manejo de variables. Las funciones internas pueden acceder a variables de la función externa (enclosing scope), pero para modificarlas necesitan la palabra clave 'nonlocal'. Sin esta declaración, Python creará una variable local en la función interna. Este concepto es fundamental para entender closures y decoradores. El enclosing scope persiste incluso después de que la función externa haya terminado su ejecución, lo que permite la creación de closures. Las variables nonlocal son especialmente útiles en patrones como factory functions y cuando se necesita mantener estado entre llamadas a funciones anidadas.
def crear_contador():
cuenta = 0
def incrementar():
nonlocal cuenta
cuenta += 1
return cuenta
def obtener_cuenta():
return cuenta
def reset():
nonlocal cuenta
cuenta = 0
return incrementar, obtener_cuenta, reset
# Crear un contador
inc, get, reset = crear_contador()
print('Cuenta inicial:', get())
print('Después de incrementar:', inc())
print('Después de incrementar:', inc())
print('Cuenta actual:', get())
reset()
print('Después de reset:', get())
Cuenta inicial: 0 Después de incrementar: 1 Después de incrementar: 2 Cuenta actual: 2 Después de reset: 0
🔧 Casos Avanzados y Buenas Prácticas
En aplicaciones profesionales, el manejo adecuado del scope es crucial para evitar efectos secundarios no deseados y mantener código limpio. Se recomienda minimizar el uso de variables globales y preferir el paso de parámetros y valores de retorno. Cuando las globales sean necesarias, deben estar claramente documentadas y tener nombres descriptivos. Los módulos actúan como espacios de nombres, permitiendo organizar código relacionado. El patrón singleton, la configuración de aplicaciones y los loggers son casos donde las variables globales pueden ser apropiadas. Es importante entender cómo funciona la importación de módulos y cómo afecta a los espacios de nombres. Las variables de clase vs instancia también siguen reglas de scope específicas que los ingenieros deben dominar.
class Config:
_instance = None
_config = {}
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = Config()
return cls._instance
def set(self, key, value):
self._config[key] = value
def get(self, key, default=None):
return self._config.get(key, default)
def configurar_app():
config = Config.get_instance()
config.set('debug', True)
config.set('database_url', 'localhost:5432')
def obtener_config(key):
config = Config.get_instance()
return config.get(key)
# Uso del patrón
configurar_app()
print('Debug mode:', obtener_config('debug'))
print('Database:', obtener_config('database_url'))
print('Missing key:', obtener_config('missing_key'))
Debug mode: True Database: localhost:5432 Missing key: None
Ejercicios Prácticos
Contador con Historial
INTERMEDIODescripción:
Crear una función que retorne tres funciones: incrementar, decrementar y obtener_historial. Debe mantener un contador y un historial de todas las operaciones realizadas usando variables nonlocal.
Sistema de Variables de Entorno
INTERMEDIODescripción:
Implementar un sistema que simule variables de entorno usando variables globales. Incluir funciones para establecer, obtener y listar todas las variables, con validación de tipos.
Analizador de Scope
AVANZADODescripción:
Escribir una función que tome código Python como string y analice qué variables son locales, globales o nonlocales. Debe identificar potenciales problemas de scope.
Cache con Limpieza Automática
AVANZADODescripción:
Implementar un sistema de cache usando closures que automáticamente limpie entradas antiguas. El cache debe mantener estadísticas de hits y misses.
Resumen y Próximos Pasos
El dominio del alcance de variables y espacios de nombres es fundamental para escribir código Python profesional y mantenible. Estos conceptos permiten estructurar aplicaciones de manera eficiente, evitar errores comunes y implementar patrones de diseño avanzados. La comprensión de la regla LEGB, el uso apropiado de global y nonlocal, y las mejores prácticas en el manejo de variables son habilidades esenciales para cualquier ingeniero de software. En el siguiente tema exploraremos las funciones lambda y programación funcional, donde aplicaremos estos conocimientos de scope en contextos más avanzados como closures y decoradores.