Lathack

Scope de variables en Python

python function variables exercises

¿Qué es el scope de variables en Python?

Primero debemos entender que las variables en Python son locales por defecto. Esto quiere decir que aquellas definidas y utilizadas en el bloque de código de una función, sólo existen dentro de la misma, y no interfieren con otras variables del resto del código. A su vez, aquellas existentes fuera de una función, no son visibles dentro de la misma. En caso de que sea conveniente o necesario, una variable local puede convertirse en una global declarándola explícitamente como tal con la sentencia «global». Por lo tanto, el alcance o scope de variables en Python se refiere al área o lugar donde estas pueden ser accedidas luego de haber sido declaradas.

Ejemplos

Veamos una simple función:

variable = 8

def funcion():
  print(variable)

funcion()
print(variable)

Out:

8
8

Ahora bien, veamos un ejemplo de una variable global:

global variable

variable = 8

def funcion():
  print(variable)

variable = 4

funcion()
print(variable)

Out:

4

4

La palabra reservada “global” es la que termina avisándole al interprete que dicha variable es la que está fuera de la función. Si no se usa, el comportamiento de la variable dentro y fuera de la función es independiente. Veamos otro ejemplo:

scope de variables en python

Out:

150

 70

Como podemos ver, hemos usado los valores globales num1 y num2 para la variable resta, la cual se encuentra fuera de la función valores().

Tener en cuenta:

Una vez sepamos el funcionamiento del scope de variables en Python, podemos advertir que en este lenguaje NO es aconsejable hacer uso de variables globales, ya que disminuye la legibilidad del código (fundamental). Su uso puede producir efectos colaterales, produciendo alteraciones no previstas en una parte del programa y, por lo tanto, pueden afectar el uso del valor en otra zona. Entonces, concluimos que dificultan la forma de trabajo modular del código. También, y muy importante, no se recomienda su usoel uso por seguridad, ya que, le damos permiso a una función a que ingrese a nuestro código y pueda manipularlo, por tanto, existe el riesgo de que seamos víctima de un malware.

Paso por valor y por referencia

En muchos lenguajes de programación existen los conceptos de paso por valor y por referencia que aplican a la hora de cómo trata una función a los parámetros que se le pasan como entrada. Si usamos un parámetro pasado por valor, se creará una copia local de la variable, lo que implica que cualquier modificación sobre la misma no tendrá efecto sobre la original. Con una variable pasada como referencia, se actuará directamente sobre la variable pasada, por lo que las modificaciones afectarán a la variable original.

En Python las cosas son un poco distintas, y el comportamiento estará definido por el tipo de variable con la que estamos tratando.

Paso por valor

Veamos un ejemplo de “paso por valor”:

def funcion(entrada):
   entrada = 0
dato = 5
funcion(dato)
print(dato)

Out:

5

En el ejemplo anterior iniciamos “dato” con 5 y se lo pasamos a funcion(). Dado que Python trata a los int como pasados por valor, dentro de la función se crea una copia local, por lo que la variable original no es modificada.

Paso por referencia

A diferencia de lo anterior, no pasa lo mismo si, por ejemplo, “dato” es una lista. En este caso Python lo trata como si estuviese pasada por referencia, es decir, lo que hace que se modifique la variable original. Por ejemplo:

def funcion(entrada):
  entrada.append(40)

dato = [10, 20, 30]
funcion(dato)
print(dato)

Out:

[10, 20, 30, 40]

El ejemplo anterior nos podría llevar a pensar que si en vez de añadir un elemento a “dato”, hacemos entrada = [], estaríamos destruyendo la lista original. Sin embargo, esto no sucede.

def funcion(entrada):
   entrada = []

n = [10, 20, 30]
funcion(n)
print(n)

Out:

[10, 20, 30]

Como vemos, solo ejecutando print(n) obtenemos resultado. Es decir, no se ha modificado el valor de la variable entrada dentro de la función.

Argumentos de longitud variable

Imaginemos que queremos una función suma() como la de los ejemplos, pero en este caso necesitamos que sume todos los números de entrada que se le pasen, sin importar si son 3, 100 o 1000. Una primera forma de hacerlo sería con una lista:

def suma(numeros):
    entrada = 0

    for j in numeros:
        entrada += j
    return entrada

print(suma([10,0,5,4]))

Out:

19

La propuesta es válida, cumple con nuestro requisito, pero realmente no estamos trabajando con argumentos de longitud variable. En realidad, tenemos un solo argumento, una lista. Sin embargo, Python tiene una solución muy potente y sencilla a la vez. Si declaramos un argumento con * (asterisco típicamente acompañado con el nombre args, pero no es obligatorio usar ese nombre).

Al declarar la función con un parámetro *, hará que los argumentos, sean la cantidad que fueran, lleguen individualmente y que una vez recibido la función lo empaquete en una tupla de manera automática.

Atención:

No debemos confundir * con los punteros en otros lenguajes de programación, no tiene nada que ver ¡En Python existen los punteros a memoria!

def suma(*args):

    print(type(args))

    print(args)

    total = 0

    for n in args:

            total += n

            return total

print(suma(10,0,5,4))

Out:

<class ‘tuple’>
(10,0,5,4)
19

Otras opciones

Usando doble ** es posible también tener como parámetro de entrada una cantidad arbitraria de elementos almacenados en forma de clave y valor (diccionario). El doble asterisco ** (usualmente acompañado por el nombre kwargs) captura cualquier keyword argument que no haya sido definido junto con la función. Los argumentos capturados por este operador son almacenados, como dijimos, en un diccionario que tiene como claves los strings que representan los nombres de los argumentos, y como valor, el valor del argumento. Este operador debe ir al final de la definición de otros parámetros, si los hubiera.

Ejemplo:

def sumar(**kwargs):
   print(type(kwargs))
   print(kwargs)
   total = 0
   for n in kwargs:
      total+= kwargs[n]
   print(total)

sumar(a=5, b=20, c=23)

Out:

<class ‘dict’>
{‘a’: 5, ‘b’: 20, ‘c’: 23}
48

Orden para usar diferentes argumentos

Si tuviéramos una función que hiciera uso de diferentes tipos de argumentos, deberíamos usarlos con un cierto orden:

1) Argumentos posicionales (argumentos comunes).

2) Los argumentos arbitrarios posicionales (*args).

3) Los keywords arguments (argumentos por defecto).

4) Un número arbitrario de keywords arguments (**kwargs). En caso de usarlos de otra manera el resultado puede ser un error, o no poder hacer uso correcto del argumento por defecto.

Por ejemplo:

scope de variables en python

Out:

3

2

()

100

{}

Funciones de orden superior en Python

Podemos crear funciones que pueden tomar otras como parámetros y también retornar (devolver) funciones como resultado. Una función que hace ambas cosas, o alguna de ellas, las llamamos funciones de orden superior. Ejemplo de estas pueden ser built-in, las cuales vienen incorporadas por defecto en Python. como pueden ser map() y filter().

En el siguiente ejemplo genérico, se pasa una función y una lista para que la función superior() trabaje:

scope de variables en python

Out:

[102, 105, 110]

[4, 25, 100]

Funciones Lambda o anónimas

Las Funciones Lambda (también llamadas anónimas) son un tipo de funciones que típicamente se pueden escribir en una línea y cuyo código a ejecutar suele ser pequeño. Resulta complicado explicar las diferencias porque, generalmente, la diferencia radica en cómo se escribe, es decir, la sintaxis. Para que tengamos una idea, son funciones que queremos usar y descartar. Básicamente se usan como argumento en funciones de orden superior.

La función cuadrado(x) que usamos en el ejemplo anterior la podríamos escribir como:

lambda x : x**2

Y la de sumar(x):

lambda x : x+100

Y con la función superior(funcion,lista) quedaría así:

print(superior(lambda x : x**2 , numeros))

print(superior(lambda x : x+100 , numeros))

Out:

[102, 105, 110]

[4, 25, 100]

A este tipo de funciones también se les puede asignar un nombre como si fuera una variable (en realidad, como si fuera un objeto). Esto último no es muy aconsejable y no fueron pensadas para esto. Pero si estamos en la consola interactiva haciendo pruebas, puede ser de gran utilidad para definir pequeñas subrutinas.

 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

7 + siete =

Lathack
Scroll al inicio