¿Qué significa el alcance en Python?

El alcance en programación es la zona del código en la cual está accesible un elemento.
Concretamente, en Python existen dos tipos principales de alcance: el alcance global y el alcance local.
No obstante, también podemos encontrar el tipo de alcance enclosing (encerrado) y el built-in (predefinido).
Entonces, nos encontramos ante la regla LEGB. Se trata de una forma de denominar a los diferentes niveles de alcances de Python.

- Local scope.
- Enclosed scope.
- Global scope.
- Built-in scope.
El alcance, también se conoce con el término “ámbito”.
El término en inglés, para referirse al alcance o al ámbito, es scope.
Espacio publicitario
En Python, los siguientes elementos crean alcances:
- Módulo
- Clase
-
Funciones (
def
ylambda
) -
Comprensiones (
list
,set
ydict
) - Expresiones generadoras
También he de comentar que hay alcances especiales como el de los bloques
except
, que contiene una referencia a la excepción generada por una declaracióntry
.
Espacio publicitario
Espacios de nombres
Con el fin de entender correctamente lo que es el alcance, primero debes conocer lo que son los namespaces, conocidos en español como espacios de nombres.
Los namespaces o espacios de nombres, son contenedores o colecciones de identificadores únicos que permiten organizar el código en áreas, y evitar conflictos de nombres.
En Python, los espacios de nombres están almacenados en diccionarios. Cada módulo tiene sus propios diccionarios de espacios de nombres.
En los diferentes apartados que tienes a continuación, te muestro como acceder a cada uno. Considero que ver las cosas en la consola, ayuda a tener una visión clara sobre los conceptos.
Alcance local
El alcance local es el alcance que tiene un elemento dentro de una función.
Este alcance se aplica también en cierto modo a las clases, aunque debo detallar, que es algo diferente. Recuerda el encapsulamiento y los tipos de acceso a miembros (público, protegido (
_
) y privado (__
)).
Por este tipo de alcance, no podemos acceder a una variable que está dentro de una función.
A continuación, tienes un ejemplo:
def imprimir_nombre():
nombre = "Programación Fácil"
print(nombre)
imprimir_nombre()
Programación Fácil
Al llamar a esta función, se ejecuta correctamente el
print()
que lleva en su interior. Por lo
tanto, acabamos viendo el valor de la variable
nombre
en la consola.
Espacio publicitario
La variable nombre
de este ejemplo, se trata
de una variable con alcance local. Esta no puede accederse
directamente desde fuera del bloque de código de la
función. Está contenida dentro de ella.
Si intentamos acceder desde fuera, recibiremos un error.
Aquí tienes un ejemplo:
def imprimir_nombre():
nombre = "Programación Fácil"
print(nombre)
NameError: name 'nombre' is not defined.
Error de nombre: el nombre 'nombre' no está definido.
El error nos indica que el intérprete de Python no está encontrando la variable
nombre
, concretamente en la línea delprint()
. Es como si no estuviera definida en el código.
Diccionario local
Ahora, si queremos acceder al diccionario del alcance
local, lo haremos mediante la función predefinida de
Python llamada locals()
:
def funcion():
a = 10
b = "Hola"
c = 10.56
# Imprimir las variables locales
print(locals())
funcion()
{'a': 10, 'b': 'Hola', 'c': 10.56}
Ten en cuenta que si utilizas
locals()
fuera de una función, a nivel de módulo, te va a devolver el diccionario global.
Alcance global
El alcance global es el alcance que tienen los elementos que se definen fuera de cualquier función. Estos elementos son accesibles, incluso desde dentro de funciones, y bloques de código como los que tienen los condicionales o bucles.
En el siguiente ejemplo puedes apreciar este tipo de alcance:
nombre = "PCMaster"
def imprimir_nombre():
nombre = "Programación Fácil"
print(nombre)
imprimir_nombre()
Programación Fácil
En primera instancia, la variable nombre
ha
sido declarada fuera de la función, lo que le da un
alcance global. Con ello, es posible “reasignarla” dentro
de la función.
Al menos, esto parece ser lo que está ocurriendo.
Espacio publicitario
Al hacer esto, estamos realizando una mala práctica. No hay fallos, pero estamos enmarañando el código.
Para que comprendas el porqué de lo que estoy diciendo, es necesario que atiendas a la advertencia (warning) que está apareciendo al hacer esa “reasignación” dentro de la función:
Redefining name 'nombre' from outer scope (line x).
Para ver los errores, advertencias e informaciones en la consola y en el código Python, deberás instalar las extensiones Pylance, Pylint y Error Lens, si estás utilizando Visual Studio Code.
La advertencia está indicando que se está “redefiniendo”
el nombre de variable nombre
, desde un
alcance externo a la función.
Esto quiere decir que no se está reasignando la variable, si no, que estamos generando otra distinta dentro de la función.
Entonces, tenemos dos variables diferentes, que se llaman igual, en el mismo código. En ningún momento se hace una reasignación.
Esto lo puedes comprobar, imprimiendo desde los dos ámbitos:
nombre = "PCMaster"
def imprimir_nombre():
nombre = "Programación Fácil"
print(nombre) # Variable local
imprimir_nombre()
print(nombre) # Variable global
Programación Fácil
PCMaster
En el ámbito global, accederás a la variable global, y en el local, a la variable local.
Espacio publicitario
Diccionario global
Para poder ver el diccionario global, solo tienes que
utilizar la función predefinida de Python denominada
globals()
, sobre el propio módulo:
nombre = "Programación Fácil"
# Imprime el diccionario de espacio de nombres global
print(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002B54B04BCB0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'e:\\Cursos\\Libro\\test.py', '__cached__': None, 'nombre': 'Programación Fácil'}
Si te fijas, es un diccionario con claves y valores. Cada clave contiene un nombre del espacio de nombres. Ese nombre tiene siempre un valor asociado, nunca está vacío. Como poco, tendrá un None de valor.
Si quieres acceder al diccionario de un módulo importado,
lo puedes hacer mediante su nombre, y accediendo a la
variable especial llamada __dict__
:
import random
print(random.__dict__)
No pongo la salida en consola debido a la longitud de esta.
La función predefinida vars()
La función predefinida vars()
devuelve el
atributo __dict__
para un módulo, clase,
instancia o cualquier otro objeto con un atributo
__dict__
.
Entonces, por ejemplo, podemos hacer esto:
class Usuario:
def __init__(self, nombre, apellido, edad):
self.nombre = nombre
self.apellido = apellido
self.edad = edad
usuario1 = Usuario("Enrique", "Barros", 32)
usuario2 = Usuario("Elvira", "Gómez", 27)
# Definición de un atributo diferente a cada objeto
usuario1.correo = "correo@gmail.com"
usuario2.telefono = "123456789"
print(vars(Usuario), "\n")
print(vars(usuario1))
print(vars(usuario2))
{'__module__': '__main__', '__init__': <function Usuario.__init__ at 0x0000025307A4D3A0>, '__dict__': <attribute '__dict__' of 'Usuario' objects>, '__weakref__': <attribute '__weakref__' of 'Usuario' objects>, '__doc__': None}
{'nombre': 'Enrique', 'apellido': 'Barros', 'edad': 32, 'correo': 'correo@gmail.com'}
{'nombre': 'Elvira', 'apellido': 'Gómez', 'edad': 27, 'telefono': '123456789'}
Puedes hacerlo de esta forma, o bien acceder a la variable
__dict__
de cada elemento, mediante la
sintaxis mostrada en un ejemplo anterior.
Por ejemplo:
print(usuario2.__dict__)
Acceder a estos diccionarios te da una visión de todo el espacio de nombres que envuelve el elemento.
El nombre
vars
proviene del término variables (en inglés).
Espacio publicitario
Alcance encerrado
El alcance encerrado es un tipo de alcance que permite que una función anidada (función hija) acceda a las variables de la función que la contiene (función padre).
def funcion_externa():
a = 10
def funcion_interna():
b = 20
print(a, b)
funcion_interna()
funcion_externa()
10 20
La palabra reservada nonlocal
La palabra reservada nonlocal
se utiliza para
trabajar con variables dentro de funciones anidadas, donde
la variable no debe pertenecer a la función interna.
En este código tengo dos variables diferentes llamadas a. Son dos variables por el hecho de encontrarse en alcances distintos:
def funcion_externa():
a = 10
def funcion_interna():
a = 100
print(a)
funcion_interna()
print(a)
funcion_externa()
100
10
Sin embargo, gracias a la palabra nonlocal
,
puedo especificar que no se cree una variable local, sino
que se utilice la de un ámbito superior.
def funcion_externa():
a = 10
def funcion_interna():
nonlocal a
a = 100
print(a)
funcion_interna()
print(a)
funcion_externa()
100
100
En el resultado, puede verse como se ha reasignado la variable de la función principal; no se ha redefinido. Esto indica que hemos podido alcanzarla.
Espacio publicitario
Con nonlocal
estamos diciéndole al intérprete
de Python que no cree una variable local dentro de la
función actual (la anidada), sino que utilice la
referencia de la variable del alcance superior, la de la
función principal, que contiene a la anidada.
El nombre nonlocal se puede traducir al español como “no local”, refiriéndose a que no se cree una variable local.
Error por falta de enlace
Si utilizamos nonlocal
para un nombre de
variable que no existe en la función principal,
recibiremos un error por falta de enlace:
def funcion_externa():
a = 10
def funcion_interna():
nonlocal b
b = 100
SyntaxError: no binding for nonlocal 'b' found
Error de sintaxis: no se encontró enlace para el nombre no local 'b'
Diccionario encerrado
Podríamos definir el alcance encerrado como un alcance
local, dentro de otro alcance local. Entonces, para ver el
diccionario encerrado, solo tienes que acceder a su
diccionario local con la función locals()
:
def funcion_externa():
a = 10
print(f"Alcance local: {locals()}")
def funcion_interna():
a = 100
print(f"Alcance encerrado: {locals()}")
funcion_interna()
funcion_externa()
Alcance local: {'a': 10}
Alcance encerrado: {'a': 100}
Alcance predefinido
El espacio de nombres built-in o predefinido contiene los elementos integrados del lenguaje. Estos están por encima incluso del ámbito global.
Espacio publicitario
Este espacio de nombres tiene el conjunto de nombres predefinidos que están disponibles en cualquier programa Python, sin la necesidad de importar ningún módulo ni tener referencias explícitas en el propio módulo.
Diccionario predefinido
Para acceder al diccionario de este espacio de nombres, podemos hacerlo así:
print(__builtins__.__dict__)
O bien, otra forma sería con la función
vars()
:
print(vars(__builtins__))
La salida en consola es muy larga, de modo que me abstengo de ponerla.
Alcance de bloque
El alcance de bloque, es un tipo de alcance que no se implementa en Python, ya que no existe de la forma tradicional.
Este alcance se da en muchos lenguajes de programación con
las llaves de bloque ({}
), elemento que no
tiene Python para este propósito.
En el alcance de bloque, las variables solo están disponibles dentro de los bloques donde se declaran, incluido cualquier otro bloque anidado dentro de ese bloque.
Entonces, por todo lo visto anteriormente, ya sabes que los alcances en Python funcionan de forma diferente.
Un ejemplo de lenguaje con alcance de bloque, es JavaScript . Por ejemplo, en Python, el bloque de código de un condicional es global. Mira el siguiente ejemplo:
if True:
nombre = "Variable declarada."
print(nombre)
Variable declarada.
Aquí estoy declarando la variable
nombre
directamente en un bloque de código de
un condicional if
. La variable es accesible
desde fuera de este.
Esto demuestra que el alcance que hay en el bloque del condicional es global. Por lo tanto, en este caso, estamos trabajando con la misma variable.
Un valor
True
en la expresión de unif
, hace que la expresión se cumpla siempre. No significa nada más.
En JavaScript , se aplica el alcance de bloque a una variable especificando la palabra reservada let o const, en la declaración de la variable.
Espacio publicitario
Aquí tienes el código equivalente del ejemplo anterior, en código JavaScript , con alcance de bloque:
test.jsif (true) {
let nombre = "Nombre reasignado.";
}
console.log(nombre);
Uncaught ReferenceError: nombre is not defined
Error Referencia no Capturada: nombre no está definido
En este caso, se está produciendo un error por variable no definida, ya que no es alcanzable desde fuera.
Pero con var
, declaro la variable sin ámbito
de bloque:
if (true) {
var nombre = "Nombre reasignado.";
}
console.log(nombre);
Nombre reasignado.
Estos últimos ejemplos son para que puedas ir comparando como funciona Python respecto a otros lenguajes de programación.
La palabra reservada global
Gracias a la palabra reservada global
,
podemos hacer que una variable global, sea utilizable
dentro de un ámbito local.
Mira el ejemplo que he escrito anteriormente:
nombre = "PCMaster" # Variable global
def imprimir_nombre():
nombre = "Programación Fácil" # Variable local
print(nombre)
imprimir_nombre()
print(nombre)
Programación Fácil
PCMaster
Aquí se están utilizando dos variables, y no puedo reasignar la variable global dentro de la función.
Espacio publicitario
Entonces, mediante el uso de global
,
conseguirás que se utilice la misma variable global dentro
de la función:
nombre = "PCMaster"
def imprimir_nombre():
global nombre
nombre = "Programación Fácil"
print(nombre)
imprimir_nombre()
print(nombre)
Programación Fácil
Programación Fácil
En ambos print()
se ha imprimido el valor que
hay en la función. Esto quiere decir que ha funcionado
correctamente. Hemos podido utilizar la misma variable
declarada fuera de la función, dentro de ella.
Además, con global
puedes especificar que una
variable local, se convierta en global, con el fin de
poder acceder a ella desde fuera:
def funcion():
global a
a = 10
funcion()
print(a)
10
Es como el comportamiento de let
y
var
de JavaScript
,
llevado al terreno de Python.
La variable sin global
, será local, por lo
tanto, equivaldría en esta comparación a una variable con
let
en JavaScript
.
global
, será global, por lo
tanto, equivaldría en esta comparación a una variable con
var
en JavaScript
.
Advertencia con la palabra global
Si utilizas Pylint en Visual Studio Code,
podrás ver la siguiente advertencia, a la hora de utilizar
la palabra reservada global
. Esta advertencia
aparece cuando evitamos una redefinición local.
Por ejemplo:
nombre = "PCMaster"
def imprimir_nombre():
global nombre
nombre = "Programación Fácil"
print(nombre)
Using the global statement
Utilizando la declaración global
Espacio publicitario
Investiga los mensajes de información, advertencia y errores
Cuando tengas avisos de las extensiones, te conviene acceder a los enlaces que proporcionan más detalles. Esto te enseñará muchas cosas que quizás desconocías.
Para acceder a estos detalles, tienes que abrir el panel de “PROBLEMAS” de Visual Studio Code, y hacer clic en cualquier enlace de cualquier aviso:

En concreto, entrando a la página de más detalles sobre la
advertencia Using the global statement, se nos
describe a la perfección porque Pylint no
recomienda utilizar global
en este caso.
La descripción que nos aparece es la siguiente (está traducida del inglés):
Ocurre cuando se utiliza la instrucción “
global
” para actualizar una variable global. Pylint desaconseja su uso.
Aquí nos expone un código problemático y después otro que considera correcto.
Código problemático:
var = 1
def foo():
global var # [global-statement]
var = 10
print(var)
foo()
print(var)
10
10
Como puedes ver, en el momento de realizar la llamada, se reasigna el valor de la variable.
Hasta no realizar la llamada, no se reasigna el valor:
var = 1
def foo():
global var # [global-statement]
var = 10
print(var)
# No se llama a la función foo()
print(var)
1
Con esto, estamos generando un código propenso a arrojar resultados incorrectos o inesperados.
Espacio publicitario
La forma que nos sugieren en la documentación de
Pylint
, es la siguiente:
Código recomendado:
var = 1
def foo():
print(var)
return 10
var = foo()
print(var)
1
10
El primer código (problemático) modifica una variable global dentro de una función, lo cual Pylint desaconseja.
El segundo código (correcto) no modifica la variable global directamente dentro de la función, sino que esta devuelve el nuevo valor, que luego se asigna manualmente. Se trata de una práctica más recomendada. Evita posibles problemas de depuración y consecuencias inesperadas.
Esto no quiere decir que no tengas que utilizar
global
. Dependiendo de como esté escrito el
código y las necesidades que tengas, puede que sea
oportuno usarla.
La decisión de usar global
debe tomarse caso
por caso, teniendo en cuenta las ventajas y desventajas.
En general, se recomienda evitar usar
global
y buscar mejores alternativas como las
del ejemplo.
Si decides usar global, asegúrate de hacerlo de manera responsable y documenta tu código para que sea comprensible para otros desarrolladores.
El tema es algo avanzado, así que no te machaques demasiado si no le acabas de ver los posibles usos. Todavía te falta aprender bastantes cosas.
Espacio publicitario
La función predefinida dir()
La función predefinida dir()
nos devuelve una
lista de nombres ordenados alfabéticamente. Estos nombres
son todos los que hay en el espacio de nombres donde se
ejecuta esta función.
Anteriormente, mostré varias cosas del funcionamiento de
dir()
. No obstante, no podía faltar en este capítulo de espacios de nombres y alcances.
Mira un ejemplo:
from math import acos
print(dir())
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos']
Primero nos aparecen los módulos integrados de Python que utiliza nuestro archivo, y luego, los elementos propios que tengas en el código.
Los nombres de módulos integrados aparecen primero, porque el guion bajo, alfabéticamente está antes que las letras.
Probemos añadiendo más cosas:
import random
aleatorio = random.randint(10,60)
print(dir())
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'aleatorio', 'random']
Si quieres hacer lo mismo, pero a nivel de función, hazlo así:
def prueba():
variable_prueba = None
print(dir())
prueba()
['variable_prueba']
Además, podrás establecer condiciones basándote en los nombres del espacio:
import math
espacio_nombres = dir()
if 'math' in espacio_nombres:
print('math está en el espacio de nombres.')
else:
print('math no está en el espacio de nombres.')
math está en el espacio de nombres.
Espacio publicitario
Espacio publicitario
Ejercicios de Python para resolver
49. En este código tenemos un elemento nombrado como
ambito
. ¿Sabrías decirme, sin ningún
print()
o return
, si se trata de
varias variables, o si es la misma?
ambito = 10
def funcion():
ambito = 15
ambito = 20
En este caso, hay dos variables diferentes llamadas
ambito
. Una es una variable global, y la
otra es una variable local de la función.
Si quieres compruébalo mediante alguna de las
diferentes formas que te he mostrado. Por ejemplo, con
una función como locals()
, o con
print()
.
50. Indica el ámbito de la variable o las variables, del código anterior.
La variable que tiene el valor 10
es de
ámbito global y la variable que tiene el valor
15
, dentro de la función, es de ámbito
local.
Espacio publicitario