28- Scope en Python

Acordeón de los Índices




Python Scope

🌐 Python Scope: La Visibilidad de las Variables

El **scope** en Python determina la visibilidad de los nombres (variables, funciones, clases, etc.) dentro de un programa. Define las regiones del código desde donde se puede acceder a un nombre en particular. Python sigue un conjunto de reglas para determinar el scope de una variable.

La comprensión del scope es fundamental para:

  • Evitar conflictos de nombres: Permite usar el mismo nombre de variable en diferentes partes del programa sin que interfieran entre sí.
  • Organizar el código: Ayuda a encapsular variables dentro de ciertas secciones del código, haciéndolo más modular y legible.
  • Gestionar la vida útil de las variables: Las variables tienen un ciclo de vida limitado por su scope.

Reglas del Scope en Python: El Acrónimo LEGB

Python utiliza la regla **LEGB** para determinar el scope de un nombre cuando se hace referencia a él:

  1. Local (L): El scope de los nombres definidos dentro de una función. Estos nombres solo son accesibles desde el interior de la función donde se definieron.
  2. Enclosing function locals (E): El scope de los nombres en las funciones envolventes (para funciones anidadas). Si un nombre no se encuentra en el scope local, Python busca en los scopes locales de todas las funciones envolventes, de adentro hacia afuera.
  3. Global (G): El scope de los nombres definidos en el nivel superior del módulo o archivo del programa. Las variables globales son accesibles desde cualquier parte del programa.
  4. Built-in (B): El scope de los nombres predefinidos en el lenguaje Python (funciones como print(), len(), excepciones como TypeError, etc.). Estos nombres siempre están disponibles.

Python busca un nombre en estos scopes en el orden LEGB. El primer lugar donde se encuentra el nombre es el scope que se utiliza.

Ejemplo Ilustrativo:

      
# Scope Global
variable_global = 10

def funcion_externa():
    # Scope de la función envolvente (Enclosing)
    variable_envolvente = 20

    def funcion_interna():
        # Scope Local
        variable_local = 30
        print(variable_local)      # Accede a la variable local (L)
        print(variable_envolvente) # Accede a la variable de la función envolvente (E)
        print(variable_global)     # Accede a la variable global (G)
        print(len("Hola"))         # Accede a la función built-in (B)

    funcion_interna()

funcion_externa()
print(variable_global)         # Accede a la variable global (G)

try:
    print(variable_local)      # Generará un NameError: variable_local no está definida en el scope global
except NameError as e:
    print(f"Error: {e}")

try:
    print(variable_envolvente) # Generará un NameError: variable_envolvente no está definida en el scope global
except NameError as e:
    print(f"Error: {e}")
      
     

En los siguientes temas, exploraremos en detalle los scopes local y global, así como las palabras clave global y nonlocal.

Local Scope

📍 Local Scope: Dentro de las Funciones

El **scope local** se refiere al ámbito de las variables que se definen dentro del cuerpo de una función. Estas variables son creadas cuando se llama a la función y dejan de existir cuando la función termina su ejecución. Por lo tanto, solo son accesibles desde el interior de la función en la que fueron definidas.

Características del Scope Local:

  • Creación y Destrucción: Las variables locales se crean al invocar la función y se destruyen al finalizar la función.
  • Accesibilidad Limitada: Solo el código dentro de la función puede acceder directamente a sus variables locales. No se puede acceder a ellas desde fuera de la función.
  • Nombres Independientes: Se pueden tener variables locales con el mismo nombre en diferentes funciones, ya que cada una existe en su propio scope local y son independientes entre sí.

Ejemplo Ilustrativo:

      
def mi_funcion():
    variable_local = "Soy local a mi_funcion"
    print(variable_local)

mi_funcion() # Output: Soy local a mi_funcion

try:
    print(variable_local) # Generará un NameError porque variable_local no está definida en el scope global
except NameError as e:
    print(f"Error: {e}")

def otra_funcion():
    otra_variable_local = 100
    print(otra_variable_local)

otra_funcion() # Output: 100

try:
    print(otra_variable_local) # Generará un NameError porque otra_variable_local no está definida en el scope global
except NameError as e:
    print(f"Error: {e}")
      
     

En este ejemplo, variable_local solo existe dentro de mi_funcion(), y otra_variable_local solo existe dentro de otra_funcion(). Intentar acceder a ellas fuera de sus respectivas funciones resulta en un NameError.

Variables Locales y Argumentos de Función:

Los parámetros que se definen en la firma de una función también se consideran variables locales dentro del cuerpo de esa función.

      
def saludar(nombre):
    mensaje = f"Hola, {nombre}!"
    print(mensaje)

saludar("Juan") # Output: Hola, Juan!

try:
    print(nombre)  # Generará un NameError
except NameError as e:
    print(f"Error: {e}")

try:
    print(mensaje) # Generará un NameError
except NameError as e:
    print(f"Error: {e}")
      
     

Aquí, tanto el parámetro nombre como la variable mensaje son locales a la función saludar().

En el siguiente tema, exploraremos el **scope global** en Python.

Global Scope

🌍 Global Scope: En la Cima del Programa

El **scope global** se refiere al ámbito de las variables que se definen fuera de cualquier función o bloque de código. Estas variables se crean cuando se inicia el programa y persisten hasta que el programa finaliza. En general, son accesibles desde cualquier parte del código, tanto dentro como fuera de las funciones.

Características del Scope Global:

  • Definición en el Nivel Superior: Las variables globales se definen en el nivel más alto del archivo del programa (o módulo).
  • Accesibilidad General: Se puede acceder a las variables globales desde cualquier función o parte del código.
  • Persistencia: Las variables globales existen durante toda la ejecución del programa.

Ejemplo Ilustrativo:

      
mensaje_global = "Soy una variable global"

def mostrar_mensaje():
    print(mensaje_global) # Accede a la variable global

mostrar_mensaje() # Output: Soy una variable global
print(mensaje_global) # Output: Soy una variable global

contador = 0

def incrementar_contador():
    global contador # Necesitamos usar 'global' para modificar la variable global
    contador += 1
    print(f"Contador ahora es: {contador}")

incrementar_contador() # Output: Contador ahora es: 1
incrementar_contador() # Output: Contador ahora es: 2
print(f"Valor global de contador: {contador}") # Output: Valor global de contador: 2
      
     

En este ejemplo, mensaje_global y contador son variables definidas en el scope global. La función mostrar_mensaje() puede acceder a mensaje_global directamente. Sin embargo, para modificar una variable global dentro de una función, se necesita utilizar la palabra clave global (como se muestra en incrementar_contador()), de lo contrario, Python trataría contador dentro de la función como una nueva variable local.

Precauciones con las Variables Globales:

Si bien las variables globales pueden ser convenientes en algunos casos, su uso excesivo puede llevar a un código más difícil de entender y mantener. Pueden hacer que las funciones sean menos independientes y más propensas a efectos secundarios no deseados. En general, se recomienda limitar el uso de variables globales y preferir pasar datos a las funciones como argumentos y devolver resultados.

La Importancia de la Palabra Clave global:

Como se mencionó en el ejemplo, si intentas asignar un valor a una variable dentro de una función, Python asumirá que es una nueva variable local, incluso si existe una variable con el mismo nombre en el scope global. Para modificar una variable global desde dentro de una función, debes declararla como global al principio de la función.

En el siguiente tema, hablaremos sobre las convenciones para nombrar variables en relación con su scope.

Naming Variables

🏷️ Nombrando Variables: Claridad y Convención

Elegir buenos nombres para las variables es una parte crucial de la programación. Los nombres de las variables deben ser lo suficientemente descriptivos para que cualquier persona (incluido tú mismo en el futuro) pueda entender fácilmente el propósito de la variable. En relación con el scope, aunque Python permite usar el mismo nombre para variables en diferentes scopes (local y global, por ejemplo), es importante hacerlo con cuidado para evitar confusiones.

Convenciones Generales para Nombres de Variables en Python:

  • Ser Descriptivo: El nombre debe indicar claramente qué almacena la variable. Por ejemplo, nombre_usuario es mejor que n.
  • Usar Snake Case: Para nombres de variables con múltiples palabras, se recomienda usar snake case, donde las palabras se separan por guiones bajos (ej. cantidad_total, indice_actual).
  • Evitar Palabras Reservadas: No uses palabras reservadas de Python (como if, for, while, def, class, etc.) como nombres de variables.
  • Ser Consistente: Mantén un estilo de nombrado coherente en todo tu proyecto.
  • Nombres Cortos para Scopes Pequeños: En scopes locales muy pequeños (como contadores en un bucle corto), nombres cortos como i o j pueden ser aceptables.

Nombres y Scope: Evitando Confusiones

Aunque es técnicamente posible tener una variable global y una variable local con el mismo nombre, generalmente se desaconseja porque puede hacer que el código sea más difícil de leer y entender. Cuando Python encuentra un nombre dentro de una función, primero busca en el scope local. Si encuentra una variable con ese nombre, la utiliza, incluso si hay una variable global con el mismo nombre.

Ejemplo de Sombreamiento de Variables (Variable Shadowing):

      
variable_compartida = "Soy global"

def mi_funcion():
    variable_compartida = "Soy local" # Esta variable local "sombra" a la global
    print(variable_compartida) # Output: Soy local

mi_funcion()
print(variable_compartida) # Output: Soy global
      
     

En este caso, dentro de mi_funcion(), variable_compartida se refiere a la variable local definida dentro de la función. La variable global con el mismo nombre queda "sombreada" y no se accede a ella dentro de la función (a menos que se use la palabra clave global).

Recomendaciones:

  • Usar Nombres Distintos: Siempre que sea posible, utiliza nombres diferentes para las variables en diferentes scopes para evitar el sombreamiento y la confusión.
  • Claridad sobre Intención: Si necesitas acceder o modificar una variable global dentro de una función, utiliza explícitamente la palabra clave global para que la intención quede clara.
  • Considerar el Contexto: Elige nombres que tengan sentido dentro del contexto de su scope. Una variable llamada contador podría ser perfectamente aceptable dentro de una función, pero un nombre más descriptivo podría ser necesario a nivel global.

En los siguientes temas, exploraremos en detalle las palabras clave global y nonlocal, que nos permiten interactuar con variables en scopes diferentes al local.

Global Keyword

🔑 La Palabra Clave global: Accediendo al Ámbito Superior

La palabra clave global se utiliza dentro de una función para indicar que una variable a la que se hace referencia debe ser buscada en el scope global. Esto es especialmente importante cuando se desea modificar el valor de una variable global desde el interior de una función. Sin la palabra clave global, cualquier asignación a una variable dentro de una función crearía una nueva variable local con ese nombre, incluso si ya existe una variable global con el mismo nombre (como vimos en el ejemplo de sombreamiento).

Cuándo Usar global:

  • Modificar una Variable Global: Si necesitas cambiar el valor de una variable que fue definida fuera de la función (en el scope global), debes declararla como global dentro de la función antes de realizar la asignación.
  • Acceder a una Variable Global (Opcional pero Recomendado para Claridad): Aunque puedes acceder al valor de una variable global dentro de una función sin usar global (Python buscará en el scope global si no la encuentra localmente), usar global al principio de la función puede hacer que tu intención sea más explícita y el código más legible.

Ejemplo de Modificación de una Variable Global:

      
contador_global = 0

def incrementar():
    global contador_global
    contador_global += 1

incrementar()
incrementar()
print(f"El valor de contador_global es: {contador_global}") # Output: El valor de contador_global es: 2
      
     

En este ejemplo, al declarar contador_global como global dentro de la función incrementar(), las operaciones contador_global += 1 modifican directamente la variable contador_global definida en el scope global.

Creación de una Variable Global Dentro de una Función (Generalmente Desaconsejado):

También es posible crear una variable global dentro de una función utilizando la palabra clave global. Si el nombre de la variable no existe en el scope global al momento de la declaración global dentro de la función, se creará una nueva variable global.

      
def crear_variable_global():
    global nueva_variable_global
    nueva_variable_global = "Soy una variable global creada dentro de una función"

crear_variable_global()
print(nueva_variable_global) # Output: Soy una variable global creada dentro de una función
      
     

Si bien esto es posible, generalmente se considera una práctica menos clara y puede dificultar el seguimiento de dónde se definen las variables globales. Es preferible definir las variables globales en el nivel superior del módulo.

Precauciones al Usar global:

El uso excesivo de la palabra clave global puede hacer que el código sea más difícil de entender y mantener, ya que las funciones se vuelven dependientes de variables definidas fuera de su scope. Esto puede reducir la modularidad y aumentar el riesgo de efectos secundarios no deseados. En muchos casos, es mejor pasar datos a las funciones como argumentos y devolver resultados para mantener las funciones más independientes y predecibles.

En el siguiente tema, exploraremos la palabra clave nonlocal, que se utiliza en el contexto de funciones anidadas.

Nonlocal Keyword

🗝️ La Palabra Clave nonlocal: Accediendo al Scope Envolvente

La palabra clave nonlocal se utiliza dentro de una función anidada para indicar que una variable a la que se hace referencia no es local a la función anidada ni global, sino que pertenece al scope de una de las funciones envolventes más cercanas.

Cuándo Usar nonlocal:

  • Modificar Variables de Funciones Envolventes: Cuando una función anidada necesita modificar una variable que está definida en el scope de su función padre (o una función abuela, etc.), pero no en su propio scope local ni en el scope global, se debe declarar esa variable como nonlocal dentro de la función anidada antes de realizar la asignación.
  • Acceder a Variables de Funciones Envolventes (Implícito): Si solo necesitas leer el valor de una variable de una función envolvente, Python la encontrará automáticamente gracias a la regla LEGB (en la 'E' de Enclosing). Sin embargo, si planeas modificarla, necesitas nonlocal.

Ejemplo Ilustrativo: Contador en Función Anidada

      
def funcion_externa():
    contador = 0

    def funcion_interna():
        nonlocal contador
        contador += 1
        print(f"Contador interno: {contador}")

    funcion_interna() # Output: Contador interno: 1
    funcion_interna() # Output: Contador interno: 2
    print(f"Contador externo: {contador}") # Output: Contador externo: 2

funcion_externa()
      
     

En este ejemplo:

  • La variable contador se define en el scope de funcion_externa().
  • Dentro de funcion_interna(), la declaración nonlocal contador permite que las operaciones sobre contador modifiquen la variable en el scope de funcion_externa().
  • Sin nonlocal, contador += 1 dentro de funcion_interna() habría creado una nueva variable local llamada contador que no afectaría a la variable en la función externa.

Diferencia entre global y nonlocal:

  • global se utiliza para acceder o modificar variables en el scope global (el nivel superior del módulo).
  • nonlocal se utiliza para acceder o modificar variables en el scope de una función envolvente más cercana (no local ni global).

Restricciones de nonlocal:

  • nonlocal solo puede referirse a variables que ya han sido definidas en el scope de una función envolvente. No se puede usar para crear nuevas variables en el scope envolvente.
  • Si la variable no se encuentra en ningún scope envolvente, se generará un error.

Ejemplo de Error si nonlocal no encuentra la variable:

      
def funcion_externa():
    def funcion_interna():
        nonlocal mensaje # 'mensaje' no está definido en el scope de funcion_externa
        mensaje = "Hola desde la interna"
        print(mensaje)

    funcion_interna() # Generará un SyntaxError o un NameError dependiendo de la versión de Python
      
     

La palabra clave nonlocal es una herramienta poderosa para trabajar con funciones anidadas y mantener el estado entre la función envolvente y la función interna.




Publicar un comentario

0 Comentarios