Python: Vida y muerte de una variable

Objetivo: entender el concepto de ámbito de una variable y la clasificación de estas en globales y locales.

En un principio el mundo era sencillo y nuestros programas simples:

# Ejemplo 1

a = 1
b = 2

print(a)
print(b)

>>> 
1
2

Nuestras variables nacían, correteaban a sus anchas por el programa y cambiaban de valor según el guión establecido.

Debemos decir que, en Python, al contrario de lo que sucede en otros lenguajes, las variables no se declaran. Se crean justo en el momento en el que reciben por asignación un valor por primera vez y, a partir de ese momento, ya pueden formar parte de una expresión. Se produce un error si una variable es utilizada antes de que realice esa asignación inicial.

Hay que matizar, además, que cuando empleamos el término variable estamos refiriéndonos en realidad a su nombre. En Python es importante diferenciar el nombre de una variable del objeto real al que referencia. Sabemos que una simple asignación, como a = 1, se ocupa de crear el objeto 1, si no existe ya, y de etiquetarlo bajo el nombre a. Es más, por cuestiones de economía, un mismo objeto puede estar etiquetado con varios nombres de variable, como muestra el ejemplo 2:

Ejemplo 2

>>> a = 1
>>> b = 1
>>> a is b
True

Las variables a y b referencian al mismo objeto. Si ahora cualquiera de ellas cambiara con una nueva asignación, este vínculo común, naturalmente, desaparecería y cada nombre etiquetaria a su respectivo objeto valor.

En este escenario de plácida convivencia introdujimos un nuevo nivel de complejidad y aparecieron las funciones, como cajas negras dispuestas a facilitarnos la reutilización del código.

# Ejemplo 3

def cajanegra():
    c = 3
    print(c)

a = 1
b = 2

print(a)
print(b)
cajanegra()

>>> 
1
2
3

Y desde ese momento empezó la jerarquización. Como sucede en nuestro mundo, en el que lo fácil o difícil que será la vida depende bastante del lugar de nacimiento, lo mismo ocurre con los nombres de variables. A las variables que residen fuera de toda función, como a y b en el ejemplo, se las demomina globales, mientras aquellas que son creadas en las funciones reciben el nombre de locales.

Las variables locales, al contrario que las globales, tienen una vida efímera. Solo existen durante el momento en el que es llamada la función. En el momento en el que esta concluye, desaparecen, pasando por el programa sin pena ni gloria.

Además, solo son visibles dentro de la función; para el resto del código son completamente inexistentes. En el ejemplo anterior, tratar de usar la variable c desde el cuerpo del programa principal ocasionaría un error.

La zona del programa en la que una variable puede ser utilizada es lo que se conoce como su ámbito. Decimos que el ámbito de una variable local está limitado al código de la función en la que está definida.

Para ilustrar este concepto con claridad, vamos a construir un escenario en el que supuestamente hay un conflicto de nombres entre una variable global y otra local:

# Ejemplo 4

def cajanegra():
    c = 3
    print('La variable c dentro de la función tiene por valor', c)

a = 1
b = 2
c = 5

print(a)
print(b)
cajanegra()
print('La variable c fuera de la función tiene por valor', c)

>>> 
1
2
La variable c dentro de la función tiene por valor 3
La variable c fuera de la función tiene por valor 5

Observa que no existe tal conflicto. No tiene nada que ver la variable c de dentro de la función a la variable c de fuera, a pesar de tener el mismo nombre. Residen en espacios de nombres diferentes. Pese al hecho de haber asignado el valor 3 a la variable dentro de la función, su valor fuera de ella no se ha visto afectado en absoluto.

Las variables locales no son solo las que están dentro del cuerpo de una función. También lo son los argumentos formales empleados en su declaración.

En el ejemplo 5, las variables x, y y c son todas locales:

# Ejemplo 5

def cajanegra(x, y):
    c = x + y
    print(c)

a = 1
b = 2
cajanegra(a, b)

>>> 
3

En el momento en el la función es invocada, x e y reciben su asignación; en este caso, el valor de las variables a y b, respectivamente.

Las variables globales tienen la particularidad de que también son visibles dentro de la función:

# Ejemplo 6

def cajanegra():
    c = 3
    print(a)
    print(b)
    print(c)

a = 1
b = 2
cajanegra()

>>> 
1
2
3

Sin embargo, todo intento de modificar una variable global desde dentro de una función mediante una nueva asignación fracasa, como podemos comprobar en el ejemplo 7:

# Ejemplo 7

def cajanegra():
    c = 3
    print(a)
    b = 5
    print('La variable b dentro de la función vale', b)
    print(c)

a = 1
b = 2
cajanegra()
print('La variable b fuera de la función sigue valiendo', b)

>>> 
1
La variable b dentro de la función vale 5
3
La variable b fuera de la función sigue valiendo 2

Lo importante a comprender es que, desde el preciso momento en el que una variable recibe una asignación dentro de una función, pasa a ser calificada como local. Cuando eso ocurre, la variable tiene una visibilidad limitada al cuerpo de la función y deja de ser considerada como global si ya existía el nombre en el ámbito exterior. Por eso, en el ejemplo, la variable a es global, mientras que b y c son locales.

Dentro de una función, la misma variable no puede ser en unos momentos global y en otros local. Si hay una asignación, aunque sea posterior a su uso como variable global, la variable será considerada local y se producirá un error:

# Ejemplo 8

def cajanegra():
    c = 3
    print(b)
    b = 5

a = 1
b = 2
cajanegra()

>>> 
Traceback (most recent call last):
  File "C:\Users\Javier\Dropbox\pythonprad\pruebas\probando.py", line 45, in <module>
    cajanegra()
  File "C:\Users\Javier\Dropbox\pythonprad\pruebas\probando.py", line 40, in cajanegra
    print(b)
UnboundLocalError: local variable 'b' referenced before assignment

El mensaje es concluyente: la variable b ha sido referenciada antes de haber recibido una asignación, algo prohibido en Python. Si no hubiese existido la asignación en la tercera línea del cuerpo de cajanegra(), b hubiese sido considerada una variable global y el codigo sería válido. Sin embargo, la asignación hace que b se trate como local, invalidando su rol global.

En numerosas ocasiones puede resultar conveniente no solo poder acceder a una variable global desde dentro de una función, sino poder cambiar su valor mediante una nueva asignación. Para lograr esto, hay que calificar la variable externa dentro de la función empleando la palabra global.

# Ejemplo 9

def cajanegra():
    c = 3
    global b
    b = 5
    print('Dentro de la función b vale', b)

a = 1
b = 2
cajanegra()
print('Fuera de la función b también vale', b)

>>> 
Dentro de la función b vale 5
Fuera de la función b también vale 5

Al calificar b como global dentro de la función estamos indicando que las asignaciones posteriores de esa variable se realizarán en un ámbito global en vez de local, modificando así el valor externo.

Hay que usar las variables globales con precaución. Permitir a las funciones que modifiquen nuestras variables externas es una práctica que puede dificultar la localización de errores cuando las cosas no funcionan como debieran. Sin embargo, son muy útiles para almacenar información de estado que luego podrá recuperarse al invocar nuevamente la función u otra diferente. En el ejemplo siguiente, utilizamos la variable global suma para retener el efecto de cada invocación a la función:

# Ejemplo 10

def sumar5():
    global suma
    suma = suma + 5
    print('La nueva suma es', suma)

suma = 0
sumar5()
sumar5()
sumar5()

>>> 
La nueva suma es 5
La nueva suma es 10
La nueva suma es 15

Cada invocación a sumar5() agrega cinco al valor de suma. La variable suma ha sido declarada como global dentro de la función para poder actualizar el valor externo, que será utilizado nuevamente al volver a llamar a la función.

Existe otro calificativo, además de global, con el que podemos etiquetar las variables dentro de una función. En el siguiente artículo lo presentaremos en sociedad.

Javier Montero Gabarró


Python: Vida y muerte de una variable


El texto de este artículo se encuentra sometido a una licencia Creative Commons del tipo CC-BY-NC-ND (reconocimiento, no comercial, sin obra derivada, 3.0 unported)


El Club del Autodidacta


Consulta el índice completo de artículos relacionados con Python.

23 opiniones en “Python: Vida y muerte de una variable”

  1. Muy bueno este artículo. No sabía que asignar un valor a una variable dentro de una función la convertía automáticamente en local aunque ya se hubiera declarado fuera. Seguro que me has ahorrado un montón de dolores de cabeza y desesperaciones en el futuro.

    1. Python tiene un montón de sutilezas que es necesario entender para ahorrarse quebraderos de cabeza.
      Una advertencia: cuidado con los objetos mutables, como las listas. Son modificables in-situ dentro de la función. Si no deseas que el objeto externo se vea afectado no te olvides de pasar una copia.
      Es algo que trataré con más detalle en otro artículo.
      Saludos

    1. Los argumentos, o parámetros formales, son aquellos que están presentes en la declaración de la función.

      La cuestión es simplemente terminológica. Mucha gente prefiere distinguir parámetro de argumento, refiriéndose primero al presente en la declaración y el segundo al valor efectivamente pasado. Otros usan ambos términos indistintamente.

      Lo verdaderamente importante es que tengas claro a cuál nos estamos refiriendo.

      Saludos

  2. Javier:
    ya sabes que estoy disfrutando mucho con tu blog
    algunas entradas son fabulosas y esta es una de ellas.
    En el ejemplo 2 creo que quedaría aun mas claro si en lugar de preguntar “a is b”
    preguntaras por “id(a)” e “id(b)” . ¡¡Fantástico!! son la misma posición

    1. ¡Me alegro de que estés disfrutando, Rosalía! 🙂
      Yo también guardo alguna de mis entradas como favoritas. Intento dar un toque de personalidad a cada una, aunque no siempre es fácil en un tema tan trillado como enseñar a programar.
      La mayor diversión que se obtiene con Python, desde mi punto de vista, más que aprendiendo y enseñando, es programando. Independientemente del nivel que uno tenga, programar en Python es siempre algo fascinante y tremendamente adictivo.
      ¡Saludos!

  3. me puse a escribir “en Python estamos protegidos de los efectos laterales del uso global
    a) En cuanto asigno valor, la variable pasa a ser local.
    b) si explicito “global” es que deseo el efecto lateral”
    FELIZ!!
    Ojo: lo estamos en los tipos simples y los inmutables

    No lo estamos en las mutables. Sabemos que podemos modificar el contenido de las mutables de otras formas que no son asignación (de la variable en su conjunto). Y estos cambios son visibles fuera

    def cajaNoTanNegra():
    lista[0]=3
    lista.append(‘a’)
    print(‘dentro:’, lista)

    lista=[1,2,3]
    print(‘antes’,lista)
    cajaNoTanNegra()
    print(‘despues’,lista)

    Como era de esperar na vez entendido el concepto de asignación en Python, el módulo llamante ve las modificaciones de la “cajaNoTanNegra”, como es deseable que sea una función.

    antes [1, 2, 3]
    dentro: [3, 2, 3, ‘a’]
    despues [3, 2, 3, ‘a’]

    1. Me gustan tus comentarios porque siempre enriquecen las entradas, Rosalía. 🙂
      Cuidado con la terminología. En Python hay que pensar de un modo diferente a como estamos acostumbrados en estos lenguajes. No existen variables mutables e inmutables. Todas son mutables en el sentido de que pueden referenciar arbitrariamente objetos diferentes en cualquier momento, aún siendo de tipos diferentes. Lo que puede ser mutable o inmutable es el objeto al que referencian las variables.
      Naturalmente, desde dentro de cualquier función tu puedes modificar directamente un objeto referenciado fuera de ella si este es mutable, pero NO puedes modificar el contenido de la variable, que no es más que una referencia a un objeto.
      En tu ejemplo, no has modificado la variable lista, que sigue apuntanto al mismo objeto tanto dentro como fuera de la función, aunque efectivamente hayas modificado su constitución al tratarse de un objeto mutable.
      Es cierto que muchas veces empleamos el término variable en un sentido clásico, como en el de otros lenguajes como C, y para muchos usos sirve. Pero en determinados contextos en Python hay que ser muy cuidadosos con los conceptos para no liarse.
      Saludos

  4. Entiendo que «Si hay una asignación, aunque sea posterior a su uso como variable global, la variable será considerada local y se producirá un error»:
    >>> a = 1
    >>> def renombre():
    … print(a)
    … a = 2
    … print(a)

    >>> renombre()
    Traceback (most recent call last):
    File “”, line 1, in
    File “”, line 2, in renombre
    UnboundLocalError: local variable ‘a’ referenced before assignment

    Pero luego recuerdo que se puede cambiar a nivel local el valor de una variable global:
    >>> var1 = 1
    >>> def mifuncion():
    … var1 = 3
    … print(var1)

    >>> mifuncion()
    3

    Y entonces me pregunto: ¿cuál es la lógica? No lo entiendo. ¿Me puedes ayudar?
    Gracias 🙂

    1. Qué tal, Juan:
      Desde el mismo momento en que asignaste a var1 un valor dentro de la función, var1 dejó de ser global dentro de ella para ser una variable local más.
      No te engañes, no modificaste el valor de la variable global var1, que sigue manteniendo su valor:

      javier@pradbuntu:~$ python3
      Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
      [GCC 4.8.2] on linux
      Type "help", "copyright", "credits" or "license" for more information.
      >>> var1 = 1
      >>> def mifuncion():
      ...     var1 = 3
      ...     print(var1)
      ... 
      >>> mifuncion()
      3
      >>> var1
      1
      >>>

      Saludos

  5. Estimado Javier, Es la primera vez que ingreso a este sitio, y antes de retirarme te quiero felicitar por la claridad con que expresas tus respuestas a las consultas. Comparto tu entusiasmo por Python que creo que es uno de los mejores y más dúctiles lenguajes de la actualidad, y que hace muy rápida, sencilla de entender y placentera la actividad de programar. Saludos.

  6. Genial, estoy aprendiendo a programar en Python y llevaba dos días con una astilla en el cerebro tratando de resolver un ejercicio que me cascaba por la cuestión del ámbito de las variables en este lenguaje de programación. Ahora ya sé por qué y ha sido gracias a ti. Muy buena explicación,Un saludo y enhorabuena por el blog.

  7. Es muy didáctico el artículo, pero hay un punto que sigue sin quedarme claro, y es cuáles serían motivos legítimos para usar y modificar variables globales dentro de una función. Muchas gracias.

  8. Espectacular el Tutorial y que buena didactatica tenes para explicar. una que belleza es Phyton cada vez que voy adentrando lo disfruto, como puede ser tan fácil programar en este lenguaje. El código se vuelve poesía. 😉

  9. Los subprogramas son trabajadores de un equipo que cooperan para resolver una aplicación.Si un trabajador “toma dinero del monedero de otro” (modifica el valor de sus variables), habrá problemas en el equipo, pero incluso si no modifica el contenido, pero “lo cotillea” (consulta global de variables), tampoco es apetecible.
    Lo deseable es que cada subprograma sea “respetuoso” con los demás “trabajadores”, no haciendo modificaciones globales ni consultas globales. La cooperación deben a través de los recursos que hayan acordadado por contratato (la definición de un subprograma es un contrato. “si Vd me da “argumentros de entrada”, le prometo que yo hago mi trabajo y le devuelvo los argumentos de salida), es decir, Los argumentos formales son los que han acordado compartir.

    Con un año de retraso he leído la pregunta de Gulliver. Me parece muy interesante y me gustaría profundizar en ella incluso tarde. Gulliver se pregunta “si el uso global de variables dificulta la mantenibilidad y legibilidad del código ¿Porque los lenguajes de programación lo permiten?” En primer lugar: ¡¡¡ Python es mucho mas cuidadoso y elegante, con esto- y con otras muchas situaciones- que la mayoría de los lenguajes!!!.

    Hay pocas, pero algunas situaciones en que resulta necesario “meter mano” a los recursos de otro cooperador. Veamos un ejemplo (Javier lo apunta en el artículo de hecho). Gulliver y yo compartimos una impresora. Yo mando a imprimir un documento mientras se está imprimiendo el mi amigo. ¿Saldrán en el mismo folio palabras de ambos documentos? ¡Que desastre! NO, la impresora posee un “semáforo”. Cuando Gulliver empezó a escribir, puso el semáforo “de la impresora” en rojo (modificación global). Cuando yo pedí escribir, mi programa vio que la impresora estaba ocupada. Cuando el documento de Gulliver termine, liberará la impresora (modificará globalmente una variable) que mi programa consultará. Aquí no me valdría que nos dieran, a cada usuario de la impresora una copia del semáforo (es decir, que éste fuera un argumento de entrada) y que nosotros pudiéramos modificar (argumento de salida). Solo puede haber un semáforo para la impresora.

    Por simplicidad, los ejemplos de Javier contienen solo un nivel de anidamiento de subprogramas pero, un subprograma (ya sea procedimiento-haga cosas- o función-devuelva valores-) puede llamar a otros subprogramas en varios niveles. Aprovecho para generalizar el concepto que transmite este post tan claramente y que resulta difícil a muchos programadores nóveles. Si un subprograma modifica variables del ámbito del subprograma que le llamó, también está haciendo un uso global y es igualmente grave. Cada variable tiene su ámbito, el de la pieza de código que la crea (sea el programa principal o un subprograma, usado en cualquier nivel de anidamiento de llamadas al subprograma).Así generalizado: lo que nos interesa calificar es si el “uso es” local o global.

Deja un comentario