6.1.4 Documentación
Ya hemos visto que en Python podemos incluir comentarios para explicar mejor determinadas zonas de nuestro código.
Del mismo modo podemos (y en muchos casos debemos) adjuntar documentación a la definición de una función incluyendo una cadena de texto (docstring) al comienzo de su cuerpo:
La forma más ortodoxa de escribir un docstring es utilizando triples comillas:
Para ver el docstring de una función, basta con utilizar help:
También es posible extraer información usando el símbolo de interrogación:
Importante: Esto no sólo se aplica a funciones propias, sino a cualquier otra función definida en el lenguaje.
Nota: Si queremos ver el docstring de una función en «crudo» (sin formatear), podemos usar <function>._________________ doc__ .
Explicación de parámetros
Como ya se ha visto, es posible documentar una función utilizando un docstring. Pero la redacción y el formato de esta cadena de texto puede ser muy variada. Existen distintas formas de documentar una función (u otros objetos):
Sphinx docstrings Formato nativo de documentación Sphinx.
Google docstrings Formato de documentación recomendado por Google.
NumPy-SciPy docstrings Combinación de formatos reStructured y Google (usados por el proyecto NumPy).
Epytext Una adaptación a Python de Epydoc(Java).
Aunque cada uno tienes sus particularidades, todos comparten una misma estructura:
- Una primera línea de descripción de la función.
- A continuación especificamos las características de los parámetros (incluyendo sus tipos).
- Por último, indicamos si la función retorna un valor y sus características.
Aunque todos los formatos son válidos, nos centraremos en Sphinx docstrings al ser el que viene mejor integrado con la documentación Sphinx. Google docstrings y Numpy docstrings también son ampliamente utilizados, lo único es que necesitan de un módulo externo denominado Napoleon para que se puedan incluir en la documentación Sphinx.
Sphinx
Sphinx es una herramienta para generar documentación e incluye un módulo «built-in» denominado autodoc el cual permite la autogeneración de documentación a partir de los «docstrings» definidos en el código.
Veamos el uso de este formato en la documentación de la siguiente función «dummy»:
Dentro del «docstring» podemos escribir con sintaxis reStructured Text – véase por ejemplo la expresión matemática en el tag :return: – lo que nos proporciona una gran flexibilidad.
Nota: La plataforma Read the Docs aloja la documentación de gran cantidad de proyectos. En muchos de los casos se han usado «docstrings» con el formato Sphinx visto anteriormente.
Anotación de tipos
Nivel intermedio
Las anotaciones de tipos se introdujeron en Python 3.5 y permiten indicar tipos para los parámetros de una función así como su valor de retorno (aunque también funcionan en creación de variables).
Veamos un ejemplo en el que creamos una función para dividir una cadena de texto por la posición especificada en el parámetro:
Como se puede observar, vamos anadiendo los tipos después de cada parámetro utilizando : como separador. En el caso del valor de retorno usamos el símbolo ->
Quizás la siguiente ejecución pueda sorprender:
Efectivamente como habrás visto, no hemos obtenido ningún error, a pesar de que estamos pasando como primer argumento una lista en vez de una cadena de texto. Esto ocurre porque lo que hemos definido es una anotación de tipo, no una declaración de tipo. Existen herramientas como mypy que sí se encargan de chequear estas situaciones.
Valores por defecto
Al igual que ocurre en la definición ordinaria de funciones, cuando usamos anotaciones de tipos también podemos indicar un valor por defecto para los parámetros.
Veamos la forma de hacerlo continuando con el ejemplo anterior:
Simplemente añadimos el valor por defecto después de indicar el tipo.
Nivel avanzado
Funciones interiores
Está permitido definir una función dentro de otra función:
Clausuras
Una clausura (del término inglés «closure») establece el uso de una función interior que se genera dinámicamente y recuerda los valores de los argumentos con los que fue creada:
Funciones anónimas «lambda»
Una función lambda tiene las siguientes propiedades:
- Se escribe con una única sentencia.
- No tiene nombre (anónima).
- Su cuerpo tiene implícito un return.
- Puede recibir cualquier número de parámetros.
Veamos un primer ejemplo de función «lambda» que nos permite contar el número de palabras de una cadena de texto:
Veamos otro ejemplo en el que mostramos una tabla con el resultado de aplicar el «and» lógico mediante una función «lambda» que ahora recibe dos parámetros:
Las funciones «lambda» son bastante utilizadas como argumentos a otras funciones. Un ejemplo claro de ello es la función sorted que tiene un parámetro opcional key donde se define la clave de ordenación.
Veamos cómo usar una función anónima «lambda» para ordenar una tupla de pares longitud -latitud:
Enfoque funcional
Como se comentó en la introducción, Python es un lenguaje de programación multiparadigma. Uno de los paradigmas menos explotados en este lenguaje es la programación funcional
Python nos ofrece 3 funciones que encajan verdaderamente bien en este enfoque: map(), filter() y reduce().
map()
Esta función aplica otra función sobre cada elemento de un iterable. Supongamos que queremos aplicar la siguiente función:
Figura 5: Rutinas muy enfocadas a programación funcional
Aplicando una función anónima «lambda»…
Importante: map() devuelve un generador, no directamente una lista.
filter()
Esta función selecciona aquellos elementos de un iterable que cumplan una determinada condición. Supongamos que queremos seleccionar sólo aquellos números impares dentro de un rango:
Aplicando una función anónima «lambda».
reduce()
Para poder usar esta función debemos usar el módulo functools. Nos permite aplicar una función dada sobre todos los elementos de un iterable de manera acumulativa. O dicho en otras palabras, nos permite reducir una función sobre un conjunto de valores. Supongamos que queremos realizar el producto de una serie de valores aplicando este enfoque:
Aplicando una función anónima «lambda».
Consejo: Por cuestiones de legibilidad del código, se suelen preferir las listas por comprensión a funciones como map() o filterQ, aunque cada problema tiene sus propias características y sus soluciones más adecuadas.