PUERTITO DE EL SAUZAL, ESCOLLERA EN LA COSTA DE EL SAUZAL QUE SIRVIÓ COMO EMBARCADERO PARA LOS HABITANTES DEL MUNICIPIO DESDE EL SIGLO XVI EN ADELANTE. NORTE DE TENERIFE. |
La sintaxis prototipo para crear una función definida por el usuario, contenga o no variables locales indistintamente, es tal y como se muestra en el siguiente esquema.
Si hemos visitado las entradas, seguro que nos suena. ¡Claro que sí! Las hemos utilizado cuando tratamos el tema de los módulos. Ya no nos parecen tan extrañas, ¿verdad? De hecho, apenas tienen complejidad y pronto aprenderemos a construirlas como churros.
Por el momento, vamos a perfilar mejor las características sintácticas de la función. Comprobamos que toda función contiene una cabecera (header), donde se coloca la sentencia def ('to define', "definir") que permite la creación de la función. A la sentencia le sigue un nombre, el nombre que le vamos a dar a la función, que, de manera análoga a lo que se recomendaba para la nominación de las variables, en la medida de lo posible, debe hacer referencia a lo que la función debe hacer. A continuación, los inevitables paréntesis que, como ya sabemos, toda función que se precie y sea del tipo que sea y en todos los lenguajes de programación deben llevar, porque en algún sitio tenemos que colocar los objetos "de partida", los PARÁMETROS, de los que ya hablaremos, con los que va a trabajar y que, subsidiariamente, identifica claramente a la vista del programador cualquier objeto función en un código.
En efecto, entre los paréntesis que, por cierto, también reciben el nombre de "zona de parámetros", debemos insertar los parámetros que debe llevar la función para que haga su trabajo y se ejecute correctamente, aunque podemos decidir que nuestra función no lleve argumento alguno (función vacía). Sin embargo, como vamos a ver en el ejemplo que mostramos a continuación, ni se nos ocurra pasar como argumento la sentencia None, si queremos indicarle al intérprete de Python que nuestra función no lleva parámetro alguno,. ¿Por qué? Porque es un sinsentido: la sentencia None quiere decir "ninguno". Estrictamente. Si decimos que una función no puede llevar argumento alguno, dicha función no puede hacer nada, carecerá de funcionalidad alguna, vulnerando su propia naturaleza. Una nevera sin gas refrigerante, un coche sin volante, un lápiz sin punta... un cacharro inútil, vamos. El hecho de que una función muestre vacío sus paréntesis indica que sus parámetros los tomará del y desde el propio programa, por ejemplo, a través de una pregunta al usuario (input()) cuya respuesta, por supuesto, desconocemos a priori; a partir de una o varias variables globales, como vimos en la página anterior; etc.
En efecto, entre los paréntesis que, por cierto, también reciben el nombre de "zona de parámetros", debemos insertar los parámetros que debe llevar la función para que haga su trabajo y se ejecute correctamente, aunque podemos decidir que nuestra función no lleve argumento alguno (función vacía). Sin embargo, como vamos a ver en el ejemplo que mostramos a continuación, ni se nos ocurra pasar como argumento la sentencia None, si queremos indicarle al intérprete de Python que nuestra función no lleva parámetro alguno,. ¿Por qué? Porque es un sinsentido: la sentencia None quiere decir "ninguno". Estrictamente. Si decimos que una función no puede llevar argumento alguno, dicha función no puede hacer nada, carecerá de funcionalidad alguna, vulnerando su propia naturaleza. Una nevera sin gas refrigerante, un coche sin volante, un lápiz sin punta... un cacharro inútil, vamos. El hecho de que una función muestre vacío sus paréntesis indica que sus parámetros los tomará del y desde el propio programa, por ejemplo, a través de una pregunta al usuario (input()) cuya respuesta, por supuesto, desconocemos a priori; a partir de una o varias variables globales, como vimos en la página anterior; etc.
UN BOCADITO DE TEORÍA: UN LENGUAJE DE SECUENCIA DE COMANDOS ES AQUÉL QUE SE EJECUTA COMANDO POR COMANDO, INSTRUCCIÓN POR INSTRUCCIÓN, CON UN FLUJO DE EJECUCIÓN DE ARRIBA A ABAJO Y, OPCIONALMENTE, DE IZQUIERDA A DERECHA SEGÚN LA EVALUACIÓN DE LOS CONDICIONALES O, EN CONTADAS OCASIONES, LA LECTURA EN SOLENOIDE INDUCIDA POR ALGÚN ITERADOR.
ESTOS LENGUAJES SE ACOPAN COMO "CAPAS" AL FLUJO DE LECTURA DE OTROS. EN EL SUPUESTO DE UNA EJECUCIÓN DINÁMICA, COMO LA LECTURA DE UNA PÁGINA HTML/CSS POR PARTE DE UN NAVEGADOR EN UNA PÁGINA WEB, UN LENGUAJE O LIBRERÍA DE SECUENCIA DE COMANDOS DERIVADO DE JAVASCRIPT, COMO JQUERY, POR EJEMPLO, EN MUCHAS OCASIONES, NO TIENEN OTRA OPCIÓN QUE "CORRER" A LA PAR QUE EL <<RUNTIME>> DEL NAVEGADOR SOBRE LA PÁGINA Y, COMO EL NAVEGADOR LEE DE ARRIBA A ABAJO EXCLUSIVAMENTE, EL LENGUAJE QUE ACTÚE EN PARALELO COMO CAPA TENDRÁ QUE HACER LO MISMO PARA NO INCURRIR EN CUELGUES O ERRORES.
POR ESO, CUANDO EN UN LENGUAJE DE SECUENCIA DE COMANDOS SE CONSTRUYE UNA FUNCIÓN, ESTA SE EJECUTA SOBRE LA MARCHA, EN EL MOMENTO EN QUE EL FLUJO DE EJECUCIÓN LLEGA HASTA ELLA. EN CONSECUENCIA, COMO EN MUCHOS CASOS EL CÓDIGO DE LA LIBRERÍA NO PUEDE REMONTAR EL CÓDIGO, SUS FUNCIONES NO NECESITAN TENER NOMBRE, POR LO QUE SÓLO DISPONEN DE FUNCIONES ANÓNIMAS EXCLUSIVAMENTE.
ESTOS LENGUAJES SE ACOPAN COMO "CAPAS" AL FLUJO DE LECTURA DE OTROS. EN EL SUPUESTO DE UNA EJECUCIÓN DINÁMICA, COMO LA LECTURA DE UNA PÁGINA HTML/CSS POR PARTE DE UN NAVEGADOR EN UNA PÁGINA WEB, UN LENGUAJE O LIBRERÍA DE SECUENCIA DE COMANDOS DERIVADO DE JAVASCRIPT, COMO JQUERY, POR EJEMPLO, EN MUCHAS OCASIONES, NO TIENEN OTRA OPCIÓN QUE "CORRER" A LA PAR QUE EL <<RUNTIME>> DEL NAVEGADOR SOBRE LA PÁGINA Y, COMO EL NAVEGADOR LEE DE ARRIBA A ABAJO EXCLUSIVAMENTE, EL LENGUAJE QUE ACTÚE EN PARALELO COMO CAPA TENDRÁ QUE HACER LO MISMO PARA NO INCURRIR EN CUELGUES O ERRORES.
POR ESO, CUANDO EN UN LENGUAJE DE SECUENCIA DE COMANDOS SE CONSTRUYE UNA FUNCIÓN, ESTA SE EJECUTA SOBRE LA MARCHA, EN EL MOMENTO EN QUE EL FLUJO DE EJECUCIÓN LLEGA HASTA ELLA. EN CONSECUENCIA, COMO EN MUCHOS CASOS EL CÓDIGO DE LA LIBRERÍA NO PUEDE REMONTAR EL CÓDIGO, SUS FUNCIONES NO NECESITAN TENER NOMBRE, POR LO QUE SÓLO DISPONEN DE FUNCIONES ANÓNIMAS EXCLUSIVAMENTE.
Esta cuestión la abordaremos con mayor profundidad cuando estudiemos el concepto de PASO DE PARÁMETROS. Por lo pronto nos quedamos con lo que acabamos de explicar.
Finalizamos la instrucción con dos puntos. Esto es así porque advertimos al intérprete de que lo que viene a continuación es un bloque de código exclusivo de la función (¿Hacemos un poco de memoria y recordamos que sucede lo mismo con las herramientas de control de flujo while e if/elif/else, así como con el bucle o loop for/in, señalando con los dos puntos un marco de sangrado o indentación donde se instruirá el bloque de código que le es propio?). Los dos puntos propiciarán una indentación automática donde instruiremos el código que corresponde a la función. Y este espacio en concreto recibe el nombre de cuerpo de la función. Desde un punto de vista meramente gráfico, visual, que éste es, en última instancia, el objetivo de la indentación pues el intérprete de Python, realmente, es perfectamente capaz por sí mismo de saber dónde empieza y dónde acaba una función construida por el usuario, a través del llamado pie de la función, nos permitirá como programadores identificar de un vistazo qué parte del código está dentro de la función y, en consecuencia, pertenece a ella, y qué parte del mismo es exterior a ella. Resulta muy útil, por ejemplo, para saber inmediatamente si una variable es de tipo local o global (v. VARIABLES).
ESTE SANGRADO O INDENTADO, QUE ME HE ENTERADO YO, DEBERÍA SER DE CUATRO ESPACIOS, UNA PULSACIÓN DEL TABULADOR, VAMOS, QUE POR DEFECTO SUELE AVANZAR CUATRO ESPACIOS EN BLANCO, DE ACUERDO A LAS ESPECIFICACIONES DE ESTILO DEL PEP-8 (PEPOCHO), Y QUE EL IDLE DE PYTHON, JUNTO CON LA MAYORÍA DE LOS IDES QUE TRABAJAN CON EL LENGUAJE REALIZA DE MANERA AUTOMÁTICA.
Y ya que hablamos de 'cuerpo', el cuerpo de la función es el conjunto de sentencias que configura el bloque de código, aglutinando todas aquéllas instrucciones, algoritmos y variables necesarias para que la función realice el trabajo para el que ha sido diseñada y nos devuelva un resultado funcional, correcto, lógico y/o esperado.
Para concluir, el pie de la función se corresponde con la 'última instrucción' del bloque de código de la función, con la última línea de código del cuerpo, que es donde colocamos la sentencia return. Esta sentencia se utiliza para dar por concluido el bloque de código de una función. ¿Y cuál es su funcionalidad? Retornar ('to return', "devolver"), devolver, el resultado de la ejecución del bloque de código de la función. A partir de este momento, el intérprete de Python abandona el ámbito de la función y regresa al cuerpo principal del código permitiéndonos continuar con la programación.
Señalamos que el ámbito de una función (o scope) es el trozo de código que sigue al nombre de la función y que aúna el cuerpo de la función y su pie. Consecuentemente, después del return, salimos del ámbito de la función y regresamos al cuerpo principal del programa.
No pocas veces nos interesa asegurarnos de que el resultado de la ejecución de la función es el que esperamos, por lo que podemos recurrir a "apedillar" a la sentencia return con el nombre de la variable que hemos elegido para almacenar la devolución. Haciéndolo así, conoceremos el resultado y sabremos si nuestra función ejecutó bien su trabajo o no y, según el caso, depurar o no el código.
En la imagen que sigue mostramos un ejemplo de versatilidad de las funciones def a la hora de diseñarlas.
Hasta ahora, todos los ejemplos de funciones def que hemos visto son funciones globales. Veamos ahora las funciones anidadas, esto es, que dependen unas de otras. Este tipo de funciones, anidadas o locales, como prefiramos llamarlas, son funciones que se definen dentro de otras funciones a la manera, si lo recordamos de las listas anidadas que ya hemos estudiado. Éstas tienen vida propia tan sólo dentro de la función def en que hayan sido definidas y sólo aquí. Suelen ser funciones accesorias, sencillas, con un cuerpo pelín magro en líneas de código si podemos decirlo así, de carácter auxiliar, y que contribuyen a mejorar, completar, complementar y/o facilitar la funcionalidad de la función matriz.
Podemos ver a continuación un esquema comentado de cómo hacerlo.
Podemos ver a continuación un esquema comentado de cómo hacerlo.
EN LA ENTRADA T2. CLOSURES: CIERRES DE CREMALLERA, PROFUNDIZAMOS UN POCO MÁS EN ESTE INTERESANTE ASPECTO DE LA PROGRAMACIÓN FUNCIONAL, QUE PODEMOS IMPORTAR A NUESTROS DISEÑOS ADSCRITOS A LA PROGRAMACIÓN ORIENTADA A OBJETOS.
Como hemos visto en el ejemplo anterior, es posible asignar el retorno de datos de una función def a una variable, proporcionándonos mayor solvencia y funcionalidad si tenemos que recurrir muchas veces a ellas en nuestro programa: si no colocamos argumentos, es decir, si no le asignamos un valor a los parámetros, como en el primer caso, tendremos siempre una llamada a la función para pasar los datos que deseemos en cualquier momento y operar con ellos cuando sea necesario; si ponemos ya los datos de entrada, como en el segundo caso, tendremos un valor fijo para la variable y ésta conservará su valor indefinidamente hasta que nosotros (o el curso del programa) lo modifique.
Ahora que ya sabemos lo que es una función global y una función local o anidada, conviene tener presente que Python cuenta con un gran número de funciones built-in e integradas, amén de los que recoge en su librería estándar (stdlib), y todavía más si sumamos las almacenadas en librerías de terceros. En consecuencia, es posible que esa función en concreto nos ronda por la cabeza y tengamos intención definir exista ya en cualquiera de los ámbitos mencionados, incluso entre los métodos, por lo que desde la perspectiva de la optimización del código y la reusabilidad valdría la pena echar un vistazo a la documentación online de Python por si está ya creada y la podemos importar.
Veamos, como un ejemplo más de función def, el eterno recurrente en todos los manuales de la seriación de Fibonacci:
Seguro que nos hemos percatado del detalle: ¡Oh!, ¡Dios mío! ¡No hay sentencia return! Que no cunda el pánico binario. Es perfectamente posible utilizar la función print() en lugar de la sentencia return como pie de función. Pero recordemos la característica principal de la función print(): está pensada para mostrar un resultado al programador, que éste lo vea y sea capaz de hacer un seguimiento del funcionamiento de sus códigos, pero no tiene operatividad alguna desde el punto de vista del propio código. Una función con pie en print() mostrará el resultado de la ejecución de la función, pero no lo almacenará en memoria, mientras que la sentencia return no sólo nos lo puede mostrar igualmente (si se lo pedimos amablemente, claro, como procedimos en el ejemplo anterior) sino que, además, lo guardará en memoria. Repasemos: el coche rojo...
En el siguiente ejemplo podremos ver cómo se pide "con amabilidad" las cosas a una función def con pie en return y compararla con otra similar con pie en print(). Ambas se comportan de manera opuesta.
Otra cosa en la que quizás nos hayamos fijado es que las funciones def pueden llevar parámetros o no: el resultado será el mismo. Tan solo cambia la estructura del bloque de código en base a adaptar e implementar si es preciso los valores que necesitamos para lograr una conclusión feliz. El optar por un método u otro dependerá de nosotros como programadores, normalmente, de acuerdo a la complejidad del programa que estemos desarrollando.
A pesar de todo, no cabe duda de que la opción con "paso de parámetros" es la más empleada y, con mucho, por su contrastada versatilidad (por ejemplo, si hay muchas entradas de datos por parte del usuario) y la claridad del código, la más útil, por lo que no tenemos más que recomendar su uso siempre que fuera posible.
Veamos, como un ejemplo más de función def, el eterno recurrente en todos los manuales de la seriación de Fibonacci:
Seguro que nos hemos percatado del detalle: ¡Oh!, ¡Dios mío! ¡No hay sentencia return! Que no cunda el pánico binario. Es perfectamente posible utilizar la función print() en lugar de la sentencia return como pie de función. Pero recordemos la característica principal de la función print(): está pensada para mostrar un resultado al programador, que éste lo vea y sea capaz de hacer un seguimiento del funcionamiento de sus códigos, pero no tiene operatividad alguna desde el punto de vista del propio código. Una función con pie en print() mostrará el resultado de la ejecución de la función, pero no lo almacenará en memoria, mientras que la sentencia return no sólo nos lo puede mostrar igualmente (si se lo pedimos amablemente, claro, como procedimos en el ejemplo anterior) sino que, además, lo guardará en memoria. Repasemos: el coche rojo...
En el siguiente ejemplo podremos ver cómo se pide "con amabilidad" las cosas a una función def con pie en return y compararla con otra similar con pie en print(). Ambas se comportan de manera opuesta.
Otra cosa en la que quizás nos hayamos fijado es que las funciones def pueden llevar parámetros o no: el resultado será el mismo. Tan solo cambia la estructura del bloque de código en base a adaptar e implementar si es preciso los valores que necesitamos para lograr una conclusión feliz. El optar por un método u otro dependerá de nosotros como programadores, normalmente, de acuerdo a la complejidad del programa que estemos desarrollando.
A pesar de todo, no cabe duda de que la opción con "paso de parámetros" es la más empleada y, con mucho, por su contrastada versatilidad (por ejemplo, si hay muchas entradas de datos por parte del usuario) y la claridad del código, la más útil, por lo que no tenemos más que recomendar su uso siempre que fuera posible.
LADERAS DE IFONCHE, CENTRO SUR DE TENERIFE. |
No hay comentarios:
Publicar un comentario