(Esta sección está aquí sólo porque creo que mola.
No es necesario en absoluto leerla para empezar a aprender Python)
¿Te gustan los ejercicios mentales? Si es así, si eres realmente osado, deberías echarle un vistazo al ensayo de Guido van Rossum sobre
metaclases . Si, por el contrario, prefieres que el cerebro
no te explote, igual te satisface este truquito.
Python utiliza espacios de nombres dinámicos (no léxicos). Esto quiere decir que si tienes una función como ésta:
def zumo_naranja():
return x*2
... donde una variable (en este caso
x) no está ligada a un argumento, y no se le asigna un valor desde dentro de la función, Python utilizará el valor que tenga cuando se llama a la función. En este caso:
x = 3
zumo_naranja()
Devuelve 6
x=1
zumo_naranja()
Devuelve 2
Normalmente, éste es el comportamiento deseado (aunque el ejemplo es un poco rebuscado, pues es raro acceder a las variables de este modo).
Sin embargo, a veces puede ser útil tener un espacio de nombres estático, es decir, guardar algún valor del entorno en que se crea la función. El modo de hacer esto en Python es por medio de los argumentos por omisión.
x = 4
def zumo_manzana(x=x):
return x*2
Aquí, al argumento
x se le asigna un valor por defecto que coincide con el
valor de la variable
x en el instante en que la función es definida. Por lo tanto, siempre que nadie proporcione un argumento para la función, funcionará así:
x = 3
zumo_manzana():
Devuelve 8
x = 1
zumo_manzana():
Devuelve 8
Concluyendo: El valor de
x no cambia. Si esto fuese todo lo que queríamos, podríamos limitarnos a escribir
def zumo_tomate():
x = 4
return x*2
incluso:
def zumo_zanahoria():
return 8
Sin embargo, lo
importante es que el valor de
x se toma del
entorno en el instante en que se define la función. ¿Qué utilidad tiene esto? Tomemos un ejemplo: Una función compuesta.
Queremos una función que funcione así:
from math import sin, cos
sincos = componer(sin,cos)
x = sincos(3)
Donde
componer es la función que queremos realizar y
x tiene el valor
-0.836021861538, que es lo mismo que
sin(cos(3)). Y ¿cómo lo hacemos?
Observa que estamos utilizando funciones como argumentos y eso ya es un truco en sí mismo.
Obviamente,
componer toma dos funciones como parámetros y devuelve una función que a su vez toma un parámetro. Un esqueleto de la solución podría ser:
def componer(fun1, fun2):
def interior(x):
# ...
return interior
Nos tentaría poner
return fun1(fun2(x)) dentro de la función
interior y dejarlo tal cual. No, no y no. Eso tendría resultados muy extraños. Imagina la siguiente situación:
from math import sin, cos
def fun1(x):
return x + " mundo"
def fun2(x):
return "Hola,"
sincos = componer(sin,cos) # Versión incorrecta
x = sincos(3)
Y bien, ¿qué valor tendría
x? Correcto:
"Hola, mundo". Y ¿por qué? Porque cuando se la llama, toma los valores de
fun1 y
fun2 del entorno, no los que andaban por ahí cuando se creó. Para conseguir una función correcta, sólo hay que utilizar la técnica descrita anteriormente:
def componer(fun1, fun2):
def interior(x, fun1=fun1, fun2=fun2):
return fun1(fun2(x))
return interior
Ahora sólo nos queda esperar que nadie proporcione a la función resultante más de un argumento, ya que eso nos rompería los esquemas
:). Y, a propósito, como no necesitamos el nombre
interior y sólo contiene una expresión, podemos utilizar una función
anónima, utilizando la palabra clave
lambda:
def componer(f1, f2):
return lambda x, f1=f1, f2=f2: f1(f2(x))
Espartano, pero claro. Te tiene que gustar
:)
...y si no ha entendido nada, no se preocupe. Al menos le ha convencido de que Python es más que "un lenguajillo para scripts"...
:)