ANTIGUA TAJEA (CANALES DE CONDUCCIÓN DE AGUA), YA EN DESUSO Y CONSTRUIDA EN ALTURA PARA SALVAR EL DESNIVEL DE LOS BARRANCOS EN ALGUNAS ZONAS DEL SUR DE TENERIFE. |
Hasta ahora, las funciones que hemos visto y empleado en nuestros scripts han sido funciones PREDEFINIDAS, básicamente, funciones built-in, preconstruidas por Python, para alegrarnos (y simplificarnos, claro) la vida. Todo un detalle, sí señor.
Pero a medida que nos vamos haciendo mayores en esto de programar, igual nos va entrando el gusanillo aquél de... "¿Caramba! ¿Y por qué no creo yo mismito/a un programilla, una función de ésas, vamos, que me permita conseguir un resultado con sólo introducir un argumento, los datos necesarios para hacerlo funcionar?"
Pues no sólo es lícito pensar así sino que, además, es necesario y recomendable. Si nos planteamos esta inquietud, ...¡Felicidades! Ya ha entrado en nuestro torrente sanguíneo la pulsión creativa que anima el noble espíritu del programador.
Antes de continuar, no estaría de más echarle un vistazo al siguiente esquema. Obviamente, se basa en el acto de programar. Pero una función "definida" es una función "programada" por nosotros: un módulo con sentido y criterio propios. Un programa, por así decirlo, de pleno derecho. Pequeño, menudo, mínimo, diminuto,... pero un programa hecho y derecho, al fin y al cabo. Con la colaboración necesaria entre funciones built-in y funciones definidas por el usuario se construye el esqueleto, la estructura básica de prácticamente el 100% de cualquier programa Python que circule por este mundo, desde los más sencillos a los más complejos.
ESTE ESQUEMA, COMO VEREMOS, RESULTA PERFECTAMENTE APLICABLE A NUESTRAS FUNCIONES. CONSTITUYE UNA MANERA RÁPIDA Y MUY VISUAL DE SABER CÓMO FUNCIONA Y CÓMO SE PROCEDE EN PROGRAMACIÓN, YA SEA CON PYTHON O CON CUALQUIER OTRO LENGUAJE.
Recordemos que cuando necesitamos obtener información sobre las potencialidades de una función preconstruida cualquiera, podemos acudir directamente desee el IDLE de Python a la función help("nombre_de_la_función") que nos proporcionará algo de información sobre la función que le pasemos como argumento. También contamos con la opción, automática, de nuestro viejo conocido "cuadro de diálogo", amarillo pálido, que se nos abre bajo nuestras propias narices cuando escribimos el paréntesis de apertura, (, de una función built-in cualquiera, mostrándonos en este caso una información más sucinta, más telegráfica, por así decirlo.
Todo lo antedicho redunda en el principio de "clarificación del contenido" bajo la perspectiva de conseguir un modelo de programación con Python lo más amigable posible.
Obviamente, esta funcionalidad está presente, como ya se ha comentado con anterioridad, para las funciones preconstruidas, internalizadas. ¿Pero qué sucede con las definidas por nosotros mismos? Pues que tendríamos que ser nosotros mismos los encargados de escribir esa información útil y/o aclaratoria, no sólo para recordárnoslo a nosotros mismos andando el tiempo(pongamos por caso, cuando tengamos que depurar o actualizar nuestros propios programas cada cierto tiempo) qué diantre hacía tal o cual función, sino para aquéllos sufridos colegas programadores que accedan a nuestros programas y se encuentren de sopetón con funciones que no son nativas de Python:"¿Y para qué servirá el galimatías éste que aparece aquí?", "¡Santo Dios! ¿Y éste otro?", "¿Y si le doy al botoncito rojo éste que dice <<DANGER>> con una calavera y dos tibias cruzadas debajo? ¿Qué pasará? Déjame probar..." ¡BUUUUM!
Pues esas aclaraciones, no lo olvidemos, que ayudan a clarificar el código y que, en consecuencia, aparece subrayado y en negrita en todos los manuales de buenas prácticas de programación, tan sumamente útiles tanto para nosotros mismos como para el resto de programadores, es lo que se llama DOCSTRINGS.
Los docstrings (de "doc" ("document") + "string") constituyen, de facto, auténticos comentarios como los que ya hemos tenido ocasión de ver en algunos de los ejemplos que hemos expuesto en este tutorial, con su sintaxis característica de almohadilla # más el texto que, por defecto, el IDLE nos tinta de rojo para señalar que no es algo que desempeñe funcionalidad alguna, pues Python no hace nada con ellos salvo mostrarlos, sino que nos resultan útiles a nosotros mismos como programadores.
Como no puede ser de otra manera, Python determina, más por convención con la comunidad de de programadores que por obligación normativa, la inserción y la sintaxis que debe caracterizar una docstring.
En cuanto a su ubicación en el código, el docstring debe situarse justo después de la cabecera de la función y de acuerdo al indentado/sangrado correspondiente. En cuanto a su sintaxis, el docstring se abre con comillas triples para poder hacer uso de más de una línea de código: en la primera, de manera esquematizada y a imitación de la estructura clásica que ya conocemos de los cuadros de información, incluimos los argumentos, el símbolo de retorno, -->, y el tipo de dato que devuelve; a partir de la segunda y y cuantas líneas estipulemos necesarias, desarrollamos un texto explicativo sobre lo que hace la función. Una vez estemos satisfechos, cerramos las triples comillas.
Y ya tenemos nuestro docstring que nos permite documentar nuestro código.
Aunque todavía no hemos comenzado a estudiar las funciones definidas por el usuario, crearemos una ex profeso para ver cómo podemos (y debemos) implementar un docstring en nuestro código:
En 1. podemos ver la primera línea con los argumentos y su devolución, mientras que en 2. ofrecemos una información más amplia sobre su funcionalidad. Observemos cómo Python ya recoge en memoria nuestra docstring, mostrándola en un cuadro de información a partir del paréntesis de apertura.
Aquí ya tenemos nuestro docstring presto a mostrarse en una función help(). Pero, ¡ojo!: esto ocurre mientras estemos trabajando en el mismo programa. Si queremos tenerlo disponible siempre, debemos pasar nuestra flamante función masa_atomica como un módulo *.py, aspecto que estudiaremos inmediatamente en una entrada.
A continuación escribimos el resultado que esperamos que nos devuelva la ejecución del ejemplo, tal y como observamos en el ejemplo que se muestra a continuación:
Nótese que estamos trabajando desde un módulo, ec.py, como podemos advertir en la esquina superior izquierda de la imagen para poder mostrar el proceso con mayor claridad, y no directamente dese el IDLE de Python.
Con lo que acabamos de escribir, la documentación de nuestra función se vería así de esplendorosa:
Lo que debemos hacer a continuación es importar el módulo doctest de la librería estándar de Python, y localizar entre sus métodos aquél que nos interesa: testmod() a partir de doctest, como se muestra abajo, y sin pasarle argumento alguno:
El método testmod() leerá la instrucción que hemos construido en la documentación (entre las triples comillas) a partir del prompt, y evaluará si el resultado que hemos insertado a continuación, en el ejemplo, 3750.0 coincide con la devolución e la ejecución del mismo. En puridad, lo que hace es ejecutar el ejemplo como si fuera una llamada a la función corriente y moliente, obviando que se halla inserta dentro de un comentario: Si 3750.0 es correcto, no se mostrará mensaje alguno; si no lo es, se mostrará un mensaje informativo en pantalla.
Como no aparece ningún cuadro informativo es de suponer que la solución que dimos para nuestro ejemplo coincide con el resultado de la ejecución del ejemplo por parte de testmod(). Si cometiéramos un error, por ejemplo, que el resultado propuesto por nosotros (se mostrará bajo el epígrafe expected) no coincidiera con el obtenido por testmod() cuando ejecute el ejemplo de función en el docstring (se mostrará bajo el epígrafe got) obtendríamos un cuadro informativo como el que sigue:
Estas advertencias se extienden también a los tipos de datos que devuelve la función. Imaginemos que modificamos el tipo de dato que nos devuelve nuestra función energiaCinetica, y en vez de un tipo de dato float como el que nos ha devuelto hasta ahora (3750.0), queremos que nos devuelva una string, una cadena de caracteres literales. Veamos un ejemplo de lo que queremos conseguir:
Como podemos apreciar, aquí no se nos devuelve un objeto tipo float sino una string como una casa.
Veamos qué sucede si no modificamos el tipo de dato (y, para más inri, el modelo EXACTO de string que esperamos obtener):
Sin embargo, si hacemos nuestro trabajo com es debido, con el tipo de dato adecuado y el modelo de string EXACTO, insistimos, que queremos obtener, no se mostrará información alguna, por lo que nuestro ejemplo en el docstring resultará del todo válido:
¡OJO CON LAS COMILLAS! A VECES SE PRODUCEN FALLOS Y SE NOS INFORMA DE QUE HA OCURRIDO UN ERROR CUANDO NOS PARECE QUE TODO ESTÁ CORRECTO. QUIZÁS LA CLAVE ESTÁ EN QUE NOSOTROS PASAMOS DOBLES COMILLAS EN CADA EXTREMO CUANDO PYTHON DEVUELVE, POR DEFECTO, LAS strings CON UNA SOLA COMILLA. PYTHON ES MUY TIQUISMIQUIS CON ESTO.
Nada nos limita para efectuar varias pruebas dentro de una misma documentación, sobre todo cuando se trata de documentar funciones con cierto nivel de complejidad. En síntesis, se trata de realizar un nuevo testeo (o los que queramos) a continuación del primero repitiendo el mismo procedimiento sintáctico. Veámoslo con un ejemplo donde introducimos dos pruebas, con u error "intencionado" en la segunda:
Veamos un ejemplo con una función algo más compleja que incluye bifurcaciones para seleccionar un tipo de archivo en concreto, por ejemplo, para su posterior almacenamiento en una lista dedicada. Aquí cobra más sentido efectuar múltiples testeos dado que disponemos de varias condiciones a evaluar:
PINARES DE AÑAVINGO, LADERAS DE ARAFO, SURESTE DE TENERIFE. |
MÓDULO DOCTEST. CÓMO TESTEAR NUESTRO DOCSTRING:
La librería estándar (stdlib) de Python nos provee de un módulo muy interesante para testear la idoneidad de la información que recogemos en la documentación de nuestros módulos. Este módulo se llama doctest y podemos acceder a él de manera sencilla a través de una importación absoluta como podemos estudiar en la entrada: https://conocepython.blogspot.com/2016/12/t1-importaciones-haciendo-crecer-tu.html.
El procedimiento, como veremos, resulta extraordinariamente simple: nos situamos justo debajo del comentario en sí y por encima de la triple comilla de cierre. En este espacio, construimos un prompt "artificial" pulsando tres veces el ángulo de "mayor que", dejamos un carácter en blanco (un espacio vacío, vamos) y, a continuación, el nombre de la función definida por nosotros (v. los capítulos que vienen inmediatamente a continuación de éste) con sus parámetros correspondientes (si los lleva) pasados como argumentos:A continuación escribimos el resultado que esperamos que nos devuelva la ejecución del ejemplo, tal y como observamos en el ejemplo que se muestra a continuación:
Nótese que estamos trabajando desde un módulo, ec.py, como podemos advertir en la esquina superior izquierda de la imagen para poder mostrar el proceso con mayor claridad, y no directamente dese el IDLE de Python.
Con lo que acabamos de escribir, la documentación de nuestra función se vería así de esplendorosa:
Lo que debemos hacer a continuación es importar el módulo doctest de la librería estándar de Python, y localizar entre sus métodos aquél que nos interesa: testmod() a partir de doctest, como se muestra abajo, y sin pasarle argumento alguno:
El método testmod() leerá la instrucción que hemos construido en la documentación (entre las triples comillas) a partir del prompt, y evaluará si el resultado que hemos insertado a continuación, en el ejemplo, 3750.0 coincide con la devolución e la ejecución del mismo. En puridad, lo que hace es ejecutar el ejemplo como si fuera una llamada a la función corriente y moliente, obviando que se halla inserta dentro de un comentario: Si 3750.0 es correcto, no se mostrará mensaje alguno; si no lo es, se mostrará un mensaje informativo en pantalla.
Como no aparece ningún cuadro informativo es de suponer que la solución que dimos para nuestro ejemplo coincide con el resultado de la ejecución del ejemplo por parte de testmod(). Si cometiéramos un error, por ejemplo, que el resultado propuesto por nosotros (se mostrará bajo el epígrafe expected) no coincidiera con el obtenido por testmod() cuando ejecute el ejemplo de función en el docstring (se mostrará bajo el epígrafe got) obtendríamos un cuadro informativo como el que sigue:
LAGARTO LISTADO TOMANDO EL SOL SOBRE UNA ROCA BASÁLTICA, CARACTERÍSTICO DEL CENTRO Y SUR DE TENERIFE. |
Estas advertencias se extienden también a los tipos de datos que devuelve la función. Imaginemos que modificamos el tipo de dato que nos devuelve nuestra función energiaCinetica, y en vez de un tipo de dato float como el que nos ha devuelto hasta ahora (3750.0), queremos que nos devuelva una string, una cadena de caracteres literales. Veamos un ejemplo de lo que queremos conseguir:
Como podemos apreciar, aquí no se nos devuelve un objeto tipo float sino una string como una casa.
Veamos qué sucede si no modificamos el tipo de dato (y, para más inri, el modelo EXACTO de string que esperamos obtener):
Sin embargo, si hacemos nuestro trabajo com es debido, con el tipo de dato adecuado y el modelo de string EXACTO, insistimos, que queremos obtener, no se mostrará información alguna, por lo que nuestro ejemplo en el docstring resultará del todo válido:
¡OJO CON LAS COMILLAS! A VECES SE PRODUCEN FALLOS Y SE NOS INFORMA DE QUE HA OCURRIDO UN ERROR CUANDO NOS PARECE QUE TODO ESTÁ CORRECTO. QUIZÁS LA CLAVE ESTÁ EN QUE NOSOTROS PASAMOS DOBLES COMILLAS EN CADA EXTREMO CUANDO PYTHON DEVUELVE, POR DEFECTO, LAS strings CON UNA SOLA COMILLA. PYTHON ES MUY TIQUISMIQUIS CON ESTO.
Nada nos limita para efectuar varias pruebas dentro de una misma documentación, sobre todo cuando se trata de documentar funciones con cierto nivel de complejidad. En síntesis, se trata de realizar un nuevo testeo (o los que queramos) a continuación del primero repitiendo el mismo procedimiento sintáctico. Veámoslo con un ejemplo donde introducimos dos pruebas, con u error "intencionado" en la segunda:
Veamos un ejemplo con una función algo más compleja que incluye bifurcaciones para seleccionar un tipo de archivo en concreto, por ejemplo, para su posterior almacenamiento en una lista dedicada. Aquí cobra más sentido efectuar múltiples testeos dado que disponemos de varias condiciones a evaluar:
ESCORRENTÍAS EN LOS BARRANCOS DE TENO, MACIZO DE TENO, NOROESTE DE TENERIFE. |
Buen material...Gracias
ResponderEliminarMuchas gracias, PIPE. Un placer. Saludos.
EliminarMuy buen material..
ResponderEliminar