VARIABLES

 Utilizaré un intérprete en línea en este enlace, gratis y desde donde se puede comprobar cuanto refleje yo:

   https://www.python.org/shell/

ó

https://repl.it/languages/python3 

Lectura aconsejada:

http://elclubdelautodidacta.es/wp/2013/01/python-el-calificador-de-ambito-nonlocal/

VARIABLES

En primer lugar hay que decir que Python es un lenguaje IDENTADO, y esto quiere decir que las tabulaciones que hay antes de una línea de programa es su identación y manda sobre las demás líneas.

Cuando se escribe una función, lo que sean las líneas de código que están asociadas a ella no pueden tener menos de 1 tabulación o identación que la línea desde la que parten, es decir:

a = "Casa"

if a == "casa":

    print(f"Has escrito {a}")

else:

    print('Pues debes tener una casa bonita tú también')

b = "Verde" 


En este ejemplo, la identación troncal empieza a la izquierda, en la primera columna, la función if y else requieren que los que tengan que hacer estén dentro de un tabulador de distancia de la columna en la que se encuentran ellas, y la vaiable b está identada dentro del bloque raíz del programa 

Las variables son unos parámetros que son precisos introducir para que el programa funcione.

Estas variables se van declarando conforme se escriben o bien se captan por el teclado, pero también pueden ser el resultado de operaciones entre variables definidas, etc..

Las variables se nombran siempre en en minúscula y si precisa de varias palabras se usan el guion bajo, pueden ir en cualquier parte del programa y su alcance está afectada por su identación.

Una variable siempre tendrá su valor mientras no se vuelva a definir su valor, y si están en la parte del programa bloque, entonces es una variable es global y no es necesario definirla como tal, va sin identación, y si están dentro de funciones o clases son locales.

Por ejemplo, si en una parte del tronco de programa la variable a = 'Perro' pero en una función a = 'casa', será perro para el programa pero casa para la función. Ahora bien, sin que se interfieran porque tengan el mismo nombre, salvo que en la misma identación se vuelva a definir esa variable, entonces toma ese valor hasta que cambie otra vez.

por ejemplo

a = 'Lunes'

print(a)            ---> Aquí imprimirá por pantalla Lunes

a = 'Martes'

print(a)            ---> Aquí imprimirá por pantalla Martes

Como vemos, a puede cambiar de valor dentro de su identación.

Con esto lo que se consigue es clarificar las variables, de manera que casi se convierten en constantes hasta que se cambien, y no se interfieren las variables en diferentes partes del código aunque tengan el mismo nombre.

Incluso, una variable puede tener el mismo nombre si su alcance es interior, como en una función y una clase, que las dos trabajen con una variable y las llamen igual, aunque no sea lo mismo, pero fuera de esos procesos no existirá, salvo que fuera definida pero no se interfiere con las de la función o la clase.

Para que una variable tenga acceso desde fuera de su procedimiento, como una función o una clase, hay que ponerle la palabra global antes de la variable y así lo toma como si estuviera en la parte bloque del código, es decir, que podrá ser accedida desde fuera del bloque en la que se encuentra.

por ejemplo:

km = 10
print(f'La velocidad actual es {km} km/h')
def acelerar():
global km
km += 5

acelerar()
print(f'Ahora que hemos acelerado la velocidad es {km} km/h')

a = input(" Aceleramos? ")
while a.lower() == "s" or a.lower() =="si":
acelerar()
print(f'Ahora que hemos acelerado la velocidad es {km} km/h')
a = input(" Aceleramos? ")
print("Ya hemos terminado")

a la variable km se puede acceder directamente desde while pero no desde la def acelerar, que es donde hay que definirla como global. De todas formas todas las variables son consultables desde otras funciones anidadas pero sólo modificables por la identación a la que pertenezca.

En este programa, que parece estar escrito al revés, pues parece que lo primero que pide y muestra es lo último en suceder, anida funciones y sus variables, de manera que todas son accesibles desde todos los puntos, pero cada función se ejecuta desde el tronco hasta la identación más alejada.

def f1():
def f2():
nivel2 = input('Escribe un número para este nivel 2º ')
print(nivel0, nivel1, nivel2)
nivel1 = input('Escribe un número para el nivel 1º ')
f2()
print(nivel0, nivel1)

nivel0 = input('Escribe un número para el nivel 0 ')
f1()
print(nivel0)
Escribe un número para el nivel 0 1
Escribe un número para el nivel 1º 2
Escribe un número para este nivel 2º 3
1 2 3
1 2
1

Process finished with exit code 0   
En realidad, la lógica de este programa es que lee desde arriba , asigna una variable y llama a una función f1, dentro de f1 asigno una variable distinta y llama a otra función f2 que está contenida dentro de f1, luego, para terminar la última función, imprime las tres variables porque puede acceder a ellas, sale de esta función e imprime las variables a las que tiene acceso, y sale de ésta función e imprime el valor de la única variable a la que tiene acceso.

Esto quiere decir que las variables de las funciones sólo son accesibles desde sí mismas o bien desde el anidamiento superior del que proviene, pero desde fuera no hay acceso a estas variables porque son locales.

Si pusiéramos debajo de print(nivel0) un print(nivel1) dadía un error: name 'nivel1' is not defined porque no tiene acceso a ella.

Bien, para acceder a una variable inaccesible hay que declarar:

global nombredevariable

entonces puedes consultar o modificarla.

En nuestro caso:
desde la raíz tenemos acceso a nivel0 pero no a nivel1 y nivel2
desde f1 tenemos acceso a nivel0 y nivel 1 pero no a nivel2
desde f2 tenemos acceso a las tres variables
Pongamos éste ejemplo, similar al anterior:
def f1():
nivel1 = input('Escribe un número para el nivel 1º: ')
global nivel0
nivel0 = "pp"
#print(f'has escrito {nivel1} para la variable nivel1')
def f2():
nivel2 = input('Escribe un número para este nivel 2º: ')
#print(f'has escrito {nivel2} para la variable nivel2')

print(nivel0, nivel1, nivel2)
f2()
print(nivel0, nivel1)
global nivel2
nivel0 = input('Escribe un número para el nivel 0º: ')
#print(f'has escrito {nivel0} para la variable nivel0')
f1()
print(nivel0)
Escribe un número para el nivel 0º: 5
Escribe un número para el nivel 1º: 5
Escribe un número para este nivel 2º: 5
pp 5 5
pp 5
pp

Process finished with exit code 0
Como vemos, nos pide los valores de las variables, pero dentro de f1, declara como global a nivel0, por lo que toma el valor pp en todo momento.
Ahora bien, como tiene acceso a esa variable por venir de ella, si eliminamos la línea que la delcara como global, el resultado es distinto:
def f1():
nivel1 = input('Escribe un número para el nivel 1º: ')
#global nivel0
nivel0 = "pp"
#print(f'has escrito {nivel1} para la variable nivel1')
def f2():
nivel2 = input('Escribe un número para este nivel 2º: ')
#print(f'has escrito {nivel2} para la variable nivel2')

print(nivel0, nivel1, nivel2)
f2()
print(nivel0, nivel1)
global nivel2
nivel0 = input('Escribe un número para el nivel 0º: ')
#print(f'has escrito {nivel0} para la variable nivel0')
f1()
print(nivel0)
Escribe un número para el nivel 0º: 5
Escribe un número para el nivel 1º: 5
Escribe un número para este nivel 2º: 5
pp 5 5
pp 5
5

Process finished with exit code 0

Vemos que el resultado es que la variable nivel0 tiene dos valores, una para su orígen, 5 y otro para f1 que es modificado internamente como pp.

Si quisiéramos modificar nivel1, que es una variable definida en una función, habría que hacerlo mediante nonlocal variable

En nuestro ejemplo, si desde f2 queremos modificarla, hay que declararla como nonlocal, así.

def f1():
nivel1 = input('Escribe un número para el nivel 1º: ')
#global nivel0
nivel0 = "pp"

def f2():
nonlocal nivel1
nivel1 = 'AAAAA'
nivel2 = input('Escribe un número para este nivel 2º: ')
print(nivel0, nivel1, nivel2)
f2()
print(nivel0, nivel1)

nivel0 = input('Escribe un número para el nivel 0º: ')
f1()
print(nivel0)
Escribe un número para el nivel 0º: 5
Escribe un número para el nivel 1º: 5
Escribe un número para este nivel 2º: 5
pp AAAAA 5
pp AAAAA
5

Process finished with exit code 0
Como vemos, nivel1 es una vaiable de una función y se accede a ella desde otra función anidada con nonlocal nivel1

Si accedemos a nivel1 como global:
def f1():
nivel1 = input('Escribe un número para el nivel 1º: ')
#global nivel0
nivel0 = "pp"

def f2():
global nivel1
nivel1 = 'AAAAA'
nivel2 = input('Escribe un número para este nivel 2º: ')
print(nivel0, nivel1, nivel2)
f2()
print(nivel0, nivel1)

nivel0 = input('Escribe un número para el nivel 0º: ')
f1()
print(nivel0)
Escribe un número para el nivel 0º: 5
Escribe un número para el nivel 1º: 5
Escribe un número para este nivel 2º: 5
pp AAAAA 5
pp 5
5

Process finished with exit code 0

es decir, que nivel1, sólo ha cambiado cuando se le llama desde dentro de la función que lo cambió pero no desde fuera.

"Moraleja: utiliza global para poder modificar una variable creada a nivel de módulo desde dentro de una función definida en ese módulo, aunque esté anidada dentro de otra función; utiliza nonlocal para modificar una variable creada a nivel de función desde otra función definida dentro de aquella.

Javier Montero Gabarró"