Lección Python

Contenido de la lección

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.

Demostración de diferentes espacios de nombres Copiar
# 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
Explicación: Este ejemplo muestra cómo las funciones anidadas pueden acceder a variables de espacios externos siguiendo la regla LEGB, pero no al revés.

🎯 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.

Diferencia entre variables locales y globales Copiar
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
Explicación: La función incrementar_local() crea una variable local que no afecta la global, mientras que incrementar_global() modifica efectivamente la variable global usando la palabra clave 'global'.

💡 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.

Uso de nonlocal en funciones anidadas Copiar
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
Explicación: Este ejemplo muestra cómo nonlocal permite a las funciones internas modificar variables del enclosing scope, creando un closure que mantiene estado.

🔧 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.

Patrón de configuración global con encapsulación Copiar
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
Explicación: Este patrón singleton encapsula variables globales de configuración, proporcionando acceso controlado y evitando variables globales dispersas en el código.

Ejercicios Prácticos

Contador con Historial
INTERMEDIO

Descripció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.

Pista: Usa nonlocal para modificar el contador y la lista de historial desde las funciones internas.
Sistema de Variables de Entorno
INTERMEDIO

Descripció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.

Pista: Usa un diccionario global y funciones que lo manipulen. Considera usar global para modificaciones.
Analizador de Scope
AVANZADO

Descripció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.

Pista: Necesitarás analizar el código línea por línea, identificando declaraciones de variables y palabras clave global/nonlocal.
Cache con Limpieza Automática
AVANZADO

Descripción:

Implementar un sistema de cache usando closures que automáticamente limpie entradas antiguas. El cache debe mantener estadísticas de hits y misses.

Pista: Combina nonlocal para el estado del cache con funciones que gestionen la limpieza automática basada en tiempo o tamaño.

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.

🏷️ Etiquetas:
python programación scope variables espacios de nombres ingeniería