# Funciones El objetivo de este capítulo es que sientas comodidad en el uso de las funciones. Parece sencillo pero es una tarea un tanto complicada porque, visto como nos gusta hacer las cosas, tenemos una gran cantidad de complejidad que abordar. Antes de entrar, vamos a definir una función y a usarla un par de veces: ``` python def inc(a): b = a + 1 return b ``` Si lanzamos: ``` >>> inc(1) 2 >>> inc(10) 11 ``` Si preguntamos por `b`: ``` >>> b Traceback (most recent call last): File "", line 1, in NameError: name 'b' is not defined ``` Parece que no conoce el nombre `b`. Esto es un tema relacionado con el *scope*. ## Scope Anteriormente se ha dicho que python es un lenguaje de programación con gestión automática de la memoria. Esto significa que él mismo es capaz de saber cuando necesita pedir más memoria al sistema operativo y cuando quiere liberarla. El *scope* es un resultado este sistema. Para que python pueda liberar memoria, necesita de un proceso conocido como *garbage collector* (recolector de basura), que se encarga de buscar cuando las referencias ya no van a poder usarse más para pedir una liberación de esa memoria. Por tanto, las referencias tienen un tiempo de vida, desde que se crean hasta que el recolector de basura las elimina. Ese tiempo de vida se conoce como *scope* y, más que en tiempo, se trata en términos de espacio en el programa. El recolector de basura tiene unas normas muy estrictas y conociéndolas es fácil saber en qué espacio se puede mover una referencia sin ser disuelta. Resumiendo mucho, las referencias que crees se mantienen vivas hasta que la función termine. Como en el caso de arriba la función en la que se había creado `b` había terminado, `b` había sido limpiada por el recolector de basura. `b` era una referencia *local*, asociada a la función `inc`. Puede haber referencias declaradas fuera de cualquier función, que se llaman *globales*. Éstas se mantienen accesibles desde cualquier punto del programa, y se mantienen vivas hasta que éste se cierre. Considera que el propio programa es una función gigante que engloba todo. Python define que cualquier declaración está disponible en bloques internos, pero no al revés. El siguiente ejemplo lo muestra: ``` python c = 100 def funcion(): a = 1 # Se conoce c aquí dentro # Aquí fuera no se conoce a ``` El *scope* es peculiar en algunos casos que veremos ahora, pero mientras tengas claro que se extiende hacia dentro y no hacia fuera, todo irá bien. ## First-class citizens Antes de seguir jugando con el *scope*, necesitas saber que las funciones en python son lo que se conoce como *first-class citizens* (ciudadanos de primera clase). Esto significa que pueden hacer lo mismo que cualquier otro valor. Las funciones son un valor más del sistema, como puede ser un string, y su nombre no es más que una referencia a ellas. Por esto mismo, pueden ser enviadas como argumento de entrada a otras funciones, devueltas con sentencias `return` o incluso ser declaradas dentro de otras funciones. Por ejemplo: ``` python >>> def filtra_lista( lista ): ... def mayor_que_4(a): ... return a > 4 ... return list( filter(mayor_que_4, lista) ) ... >>> filtra_lista( [1,2,3,4,5,6,7] ) [5, 6, 7] ``` En este ejemplo, haciendo uso de la función `filter` (usa la ayuda para ver lo que hace), filtramos todos los elementos mayores que `4` de la lista. Pero para ello hemos creado una función que sirve para compararlos y se la hemos entregado a la función `filter`. Este ejemplo no tiene más interés que intentar enseñarte que puedes crear funciones como cualquier otro valor y asignarles un nombre, para después pasarlas como argumento de entrada a otra función. ## Lambdas Las funciones *lambda*[^lambda] o funciones anónimas son una forma sencilla de declarar funciones simples sin tener que escribir tanto. Python las define como funciones para vagos. La sintaxis de una función lambda te la enseño con un ejemplo: ``` >>> lambda x, y: x+y at 0x7f035b879950> >>> (lambda x, y: x+y)(1,2) 3 ``` En el ejemplo primero se muestra la declaración de una función y después colocando los paréntesis de precedencia y después de llamada a función se construye una función a la izquierda y se ejecuta con los valores `1` y `2`. Es fácil de entender la sintaxis de la función lambda, básicamente es una función reducida de sólo una sentencia con un `return` implícito. El ejemplo de la función `filra_lista` puede reducirse mucho usando una función lambda: ``` python >>> def filtra_lista( lista ): ... return list( filter(lambda x: x > 4, lista) ) ... >>> filtra_lista( [1,2,3,4,5,6,7] ) [5, 6, 7] ``` No necesitábamos una función con nombre en este caso, porque sólo iba a utilizarse esta vez, así que resumimos y reducimos tecleos. De todos modos, podemos asignarlas a una referencia para poder repetir su uso: ``` python >>> f = lambda x: x+1 >>> f(1) 2 >>> f(10) 11 >>> f at 0x7f02184febf8> ``` Las funciones lambda se usan un montón como *closure*, un concepto donde el *scope* se trabaja más allá de lo que hemos visto. Sigamos visitando el *scope*, para entender sus usos más en detalle. [^lambda]: Toman su nombre del [Lambda Calculus](https://en.wikipedia.org/wiki/Deductive_lambda_calculus). ## Scope avanzado Cada vez que se crea una función, python crea un nuevo contexto para ella. Puedes entender el concepto de contexto como una tabla donde se van guardando las referencias que se declaran en la función. Cuando la función termina, su contexto asociado se elimina, y el recolector de basura se encarga de liberar la memoria de sus variables, tal y como vimos anteriormente. Lo que ocurre es que estos contextos son jerárquicos, por lo que, al crear una función, el padre del contexto que se crea es el contexto de la función madre. Python utiliza esto como método para encontrar las referencias. Si una referencia no se encuentra en el contexto actual, python la buscará en el contexto padre y así sucesivamente hasta encontrarla o lanzar un error diciendo que no la conoce. Esto explica por qué las variables declaradas en la función madre pueden encontrarse y accederse y no al revés. Aunque hemos explicado el *scope* como un concepto asociado a las funciones, la realidad es que hay varias estructuras que crean nuevos contextos en python. El comportamiento sería el mismo del que se ha hablado anteriormente, las referencias que se creen en ellos no se verán en el *scope* de nivel superior, pero sí al revés. Los casos son los siguientes: - Los módulos. Ver capítulo correspondiente - Las clases. Ver capítulo de Programación Orientada a Objetos. - Las funciones, incluidas las funciones anónimas o lambda. - Las expresiones generadoras[^generator-expression], que normalmente se encuentran en las *list-comprehension* que ya se han tratado en el capítulo previo. [^generator-expression]: ### Global Hemos hablado de qué sentencias crean nuevos contextos, pero no hemos hablado de qué pasa si esos nuevos contextos crean referencias cuyo nombre es idéntico al de las referencias globales. Partiendo de lo que se acaba de explicar, y antes de adentrarnos en ejemplos, si se crea una función (o cualquiera de las otras estructuras) python creará un contexto para ella. Una vez creado, al crear una variable en este nuevo contexto, python añadirá una nueva entrada en su tabla hija con el nombre de la variable. Al intentar consultarla, python encontrará que en su tabla hija existe la variable y tomará el valor con el que la declaramos. Cuando la función termine, la tabla de contexto asociada a la función será eliminada. Esto siempre es así, independientemente del nombre de referencia que hayamos seleccionado. Por tanto, si el nombre ya existía en alguno de los contextos padre, lo ocultaremos, haciendo que dentro de esta función se encuentre el nombre recién declarado y no se llegue a buscar más allá. Cuando la función termine, como el contexto asociado a ésta no está en la zona de búsqueda de la función madre, en la función madre el valor seguirá siendo el que era. Ilustrándolo en un ejemplo: ``` >>> a = 1 >>> def f(): ... a = 2 ... print(a) ... >>> a 1 >>> f() 2 ``` Aunque el nombre de la referencia declarada en el interior sea el mismo que el de una referencia externa su declaración no afecta, lógicamente, al exterior ya que ocurre en un contexto independiente. Para afectar a la referencia externa, python dispone de la sentencia `global`. La sentencia `global` afecta al bloque de código actual, indicando que los identificadores listados deben interpretarse como globales. De esta manera, si se reasigna una referencia dentro de la función, no será el contexto propio el que se altere, sino el contexto padre que la albergue. ``` >>> a = 1 >>> def f(): ... global a ... a = 2 ... print(a) ... >>> f() 2 >>> a 2 ``` > NOTA: Te recomiendo, de todas formas, que no edites valores globales desde > el cuerpo de funciones. Es más elegante y comprensible si los efectos de las > funciones sólo se aprecian en los argumentos de entrada y salida. Para más detalles sobre limitaciones y excepciones, puedes buscar en la ayuda ejecutando `help("global")`. ### Scope léxico, Closures Hemos dicho que las funciones pueden declararse dentro de funciones, pero no hemos hablado de qué ocurre con el *scope* cuando la función declarada se devuelve y tiene una vida más larga que la función en la que se declaró. El siguiente ejemplo te pone en contexto: ``` python def create_incrementer_function( increment ): def incrementer (val): # Recuerda que esta función puede ver el valor `increment` por # por haber nacido en un contexto superior. return val + increment return incrementer increment10 = create_incrementer_function( 10 ) increment( 10 ) # Returns 20 increment1 = create_incrementer_function( 1 ) increment( 10 ) # Returns 11 ``` En este ejemplo hemos creado una función que construye funciones que sirven para incrementar valores. Las funciones devueltas viven durante más tiempo que la función que las albergaba por lo que saber qué pasa con la variable `increment` es difícil a simple vista. Python no destruirá ninguna variable que todavía pueda ser accedida, si lo hiciera, las funciones devueltas no funcionarían porque no podrían incrementar el valor. Habrían olvidado con qué valor debían incrementarlo. Para que esto pueda funcionar, las funciones guardan el contexto del momento de su creación, así que la función `incrementer` recuerda la primera vez que fue construida en un contexto en el que `increment` valía `10` y la nueva `incrementer` creada en la segunda ejecución de `create_incrementer_function` recuerda que cuando se creó `increment` tomó el valor `1`. Ambas funciones son independientes, aunque se llamen de la misma forma en su concepción, no se pisaron la una a la otra, porque pertenecían a contextos distintos ya que la función que las creaba terminó y luego volvió a iniciarse. Este funcionamiento donde el comportamiento de las funciones depende del lugar donde se crearon y no del contexto donde se ejecutan se conoce como *scope léxico* y esta forma de implementarlo, haciendo que cada función recuerde el contexto en el que se creó se denomina *closure*. Concretamente, las *closures* son una forma de implementar el *scope léxico* en un lenguaje cuyas funciones sean *first-class citizens*, como es el caso de python, y su funcionamiento se basa en la construcción de los contextos y su asociación a una función capaz de recordarlos aunque la función madre haya terminado. A nivel práctico, las *closures* son útiles. TODO ## Argumentos de entrada ### Positional and Keyword Arguments #### Defaults > WARNING! MUTABLE DEFAULTS ## Decorators