FUNCIONES DEFINIDAS POR EL USUARIO I

LAURISILVA EN LAS LADERAS ALTAS DE CHANAJIGA, LA OROTAVA-LOS REALEJOS, CENTRO DE TENERIFE
      Vamos a hacer un ejercicio de imaginación, a ver qué tal se nos da: resulta que, por esas cosas del azar, nos han nombrado encargado/a de la sección de mantenimiento y reparaciones de una empresa XYZ.SA dedicada al alquiler de vehículos a otras empresas y particulares. Y ahí estamos nosotros, unos cuantos meses ya de trabajo en nuestras nuevas responsabilidades, dando lo mejor de nosotros mismos y encantados de la vida. ¡Oiga! Por entonces, la empresa XYZ.SA realiza una contratación porque estima que la sección necesita reforzarse con un nuevo operario. Y, claro, el chico o la chica que nos encomiendan, hombre, voluntarioso/a y despierto/a sí que es pero, enterarse, lo que se dice enterarse, más bien se entera poco. Así que para hacer las cosas más sencillas nos decidimos a ponerlo todo por escrito: cada trabajo que tenga que hacer (función) tendrá un nombre (nombre de la función que, en la medida de que fuera posible, incluirá un verbo que ayude a clarificar su cometido, como 'cambiarFiltroAceite', 'recargarBaterías' o 'comprobarPresiónNeumáticos') de modo que nuestro operario/a sepa ya de entrada por dónde van a ir los tiros y qué es lo que se le va a exigir. A continuación escribimos las instrucciones paso a paso (líneas de código/líneas de instrucción), y en el orden exacto en el que tiene que ejecutarlas (bloque de código), para concluir con la pertinente comprobación de que todo se ha hecho correctamente (testeo), esto es, que el filtro de aceite funcione, que la batería proporcione la chispa de encendido con el voltaje adecuado una vez le damos al arranque, o que los neumáticos contengan el volumen de aire suficiente para permitir una conducción óptima y segura. Lo bueno del caso, pese a todo, es que nuestro operario/a aprende rápido y basta con escribirle una sola vez las instrucciones, eso sí, bien escritas, con su gramática y su sintaxis correctas, para que, siempre que le ordenemos realizar el mismo trabajo, sólo con decirle: cambiarFiltroAceite, recargarBaterías o comprobarPresiónNeumáticos sepa ya claramente cómo proceder sin tener que volver a reescribir una y otra vez las instrucciones del proceso.
Pues bien: esto es lo que hacen las funciones def en Python. Nosotros somos los encargados, el intérprete de Python nuestro nuevo chico/a listo/a, y la función diseñada por nosotros, las instrucciones.
¿Verdad que no parece tan difícil?
Hasta ahora hemos visto y trabajado con funciones built-in, preconstruidas por el propio lenguaje y que abordan las acciones más básicas y comunes en programación. Podemos imaginar que las funciones preconstruidas son como los ladrillos, los bloques, la arena, el cemento, la cal,... con los que construimos nuestros hogares. Ahora bien, las paredes, el suelo, el techo, los huecos de las puertas y ventanas, las columnas de sustentación, etc,... eso ya es cosa nuestra. Aquéllo que configura la casa, lo que la define y da forma, el plano (nuestro proyecto de programación) nos compete a nosotros. Traducido a Python, las paredes, el suelo, el techo, los huecos de las puertas y ventanas, las columnas de sustentación, etc., se corresponderían con las funciones def (def-inidas por el usuario).
La razón de ser de cualquier lenguaje de programación.
El capítulo final de todo lo que hemos estudiado hasta ahora, junto con las excepciones, que trataremos a continuación, antes de pasar a la Programación Orientada a Objetos.


      Deducimos, pues, que en Python contamos con dos tipos básicos de funciones: las built-ins, preconstruidas por el lenguaje, y las def, definidas o construidas por el propio programador.






Las funciones definidas por el usuario harán uso de todos los elementos que hemos visto hasta ahora en este manual según corresponda, más las excepciones, amén de las funciones built-ins y las importaciones de librerías que consideremos oportunas para llevar a cabo nuestros proyectos.
Una función es un conjunto de sentencias que pueden ser invocadas las veces que fuera necesario durante la ejecución (running) de un programa. Las ventajas de su uso consisten en:
  1.  Minimización del código (optimización)
  2. Aumento y mejora de la legibilidad del código (entendimiento)
  3. Fomento de la reutilización o reusabilidad del código (eficiencia y  ahorro)

Esta definición, concisa y ajustada, la hemos tomado de 'Python 3 al descubierto', escrito por don Arturo Fernández Montero, ed. RClibros, Podemos decir también que una función viene a ser una manera reglada de conjuntar expresiones y sentencias capaces de llevar a cabo una tarea determinada.
Las funciones definidas por el usuario, llamadas funciones def, por la sentencia que da a entender a Python que vamos a construir una función, o también, funciones diseñadas, pueden ser de cinco tipos: funciones globales, funciones locales, funciones lambda, funciones recursivas y métodos (éstos últimos, sobre todo, ligados a la Programación Orientada a Objetos, POO).

      DE MODO GENÉRICO, TENGAMOS EN CUENTA QUE LAS FUNCIONES SÓLO SE EJECUTAN CUANDO SON LLAMADAS (to call, "llamar"), POR LO CUAL DEBEN POSEER LA CONDICIÓN PREVIA DE 'CALLABLE', ESTO ES, "CAPAZ DE SER LLAMADA O INVOCADA", PORQUE SEA UN CONSTRUCTO DEL LENGUAJE QUE LLEVE LA SENTENCIA def Y TENGA UN NOMBRE COMO EL MÍO, MATTY, PARA QUE SE ME PUEDA LLAMAR, COMO AHORA MISMO QUE OIGO QUE ME LLAMAN PARA JUGAR CON LA PELOTA. SI NO, ¿CÓMO SABRÍA QUE ES A MÍ A QUIEN LLAMAN Y NO A OTRO U OTRA, COMO MI AMIGUITA DORI? ¿EH? ¡GUAU!



PINOS EN LAS NIEBLAS DEL BARRANCO DEL RÍO, SUR DE TENERIFE

Las funciones en general y las definidas por el usuario en particular constituyen la mejor muestra de  su relevancia para reducir y optimizar código. Por ejemplo, veamos lo magníficamente bien que los argumentos (los trataremos en profundidad en los capítulos que siguen), como sucede en el caso de muchas de las funciones predefinidas que ya conocemos como len(iterable) o print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) pueden sustituir a los inputs:


La sentencia return, bajo la sintaxis return value1, value2, value3, ..., valuen, puede devolver tantos valores como solicitemos, pudiendo ser éstos tanto valores en sí como expresiones, en cuyo caso, es preferible envolverlas entre paréntesis. Veámoslo:




Un último apunte. Es irrelevante para programar en este lenguaje pero lo introducimos aquí a título informativo porque siempre viene bien ir más allá de la mera gramática, ortografía y sintaxis para comprenderlo mejor. Veamos. En el campo de las funciones definidas por el usuario, Python sólo cuenta con funciones con nombre. Es decir, toda función que definamos nosotros tendrá que llevar nombre sí o sí para poder llamarla (en cualquier ámbito de la vida, todo tiene un nombre para poder distinguirlo del resto de cuanto existe: mesa, microbio, pera, libro, memoria ram, planeta, bombilla, estómago, parlamento, volar, comer, cantar, etc... Incluso, nombres propios que señalan a individuos concretos sobre los genéricos de su especie: María, Pedro, Manuel, Elena, incluidos algunos animales, con nombres como Toby, Misha, Glub). Así podremos invocarla cuando la necesitemos en el transcurso de la ejecución de nuestros programas, tantas veces  desde la primera hasta la última línea de código como fuera preciso. Como ya podemos entender a estas alturas, este sistema incide positivamente en el concepto de reutilización o reusabilidad del código.
Resumiendo: Las funciones con nombre son aquéllas que pueden ser llamadas por su nombre, valga la redundancia, para ser ejecutadas cuando corresponda y cuantas veces estimemos oportuno.
Sin embargo, en otros lenguajes de programación como Javascript y sus librerías, por ejemplo, existen las llamadas FUNCIONES ANÓNIMAS que, como su nombre da a entender, no tienen nombre. Y como no tienen nombre no pueden ser llamadas, razón por la cual se ejecutan automáticamente en cuanto el flujo de ejecución llega hasta ellas. 🎓

       EN NUESTRO QUERIDO PYTHON, UNA FUNCIÓN, SEA NATIVA DE PYTHON, PREDEFINIDA POR ÉSTE O DEFINIDAS POR NOSOTROS, Y PONIENDO EL ACENTO EN ÉSTA ÚLTIMA DEVUELVE SIEMPRE UN RESULTADO: A TRAVÉS DE return, YA SEA EL ESPERADO O NO, COMO RESULTADO DE LA LECTURA Y EJECUCIÓN DEL CÓDIGO: UNA IMPRESIÓN EN PANTALLA MEDIANTE LA FUNCIÓN print(); O EL OBJETO ESPECIAL None SI NO ENCUENTRA AL PIE DE LA FUNCIÓN NI LA SENTENCIA return NI 
LA FUNCIÓN print().

      

      

      

      

      VARIABLES GLOBALES Y LOCALES:

   
      Estos dos tipos de variables, globales y locales, hacen realmente referencia al tipo de variable que integran. Ya conocemos suficientemente bien el concepto de variable, incluyendo los tipos de variable simple, (una variable, un dato) y variable compleja (una variable, una colección de datos). Ahora vamos a ver otros dos tipos de variable: variable global y variable local.
Una variable global es aquélla que, dentro de un programa cualquiera, se declara fuera del ámbito de una función definida por el usuario, esto es, que no está incluida en el cuerpo, en el bloque de código de la función; mientras que una variable local es aquélla que, dentro de un programa cualquiera, se declara dentro del ámbito de una función definida por el usuario, esto es, que está inserta en el cuerpo, en el bloque de código de la función y, por consiguiente, supeditada a ella.



Como podemos ver, las variables locales gozan de una existencia limitada, efímera, al contrario que las globales, pasando a mejor vida en el instante justo, supremo e irreversible, en que se ejecuta la función. De hecho, sólo tienen relevancia dentro de los márgenes de la propia función deviniendo intrascendente para el resto del código, incluido el resto de funciones definidas por el usuario.
Veamos en el siguiente ejemplo como dos variables con el mismo nombre y valor, se comportan de distinta manera si se almacenan en una variable  global o, por el contrario, lo hacemos en una local.



Observemos en el ejemplo que se muestra a continuación cómo una función def es incapaz de operar con variables globales y locales al mismo tiempo, dando preponderancia a la global, que sí reconoce, pero no así a la local, incluida como debe ser dentro del ámbito de la función.



Comprobamos que sí lo hace, sin embargo, cuando partimos de dos variables globales y ninguna local o, al contrario, con dos variables locales y ninguna global, arrojando en ambos casos el mismo resultado.
Y como sería de esperar aplicando aplicando lo que ya sabemos, el hecho de contar con variables globales y locales idénticas no obsta para que la función corra y nos devuelva el resultado que esperamos:



Llegados a este punto debemos resaltar el hecho de que cuando se declara una variable dentro de una función, es decir, cuando le asignamos un valor, bien fuera uno solo (variable simple) como una colección de datos (variable compuesta o compleja), ya fuera ésta una lista, una cadena, una tupla, un diccionario, etc., la variable pasa a ser considerada por el intérprete de Python como local y, en consecuencia, estrictamente limitada al ámbito de la función y sólo visible para sí misma.
Aclarado esto, Python nos proporciona la posibilidad de modificar una variable global dentro del propio cuerpo de la función mediante el uso de la sentencia global de la manera que representamos en el ejemplo.
Debemos tener cuidado, sin embargo, en no introducir parámetros en la función con el mismo nombre de la variable (el mismo namespace) pues entonces la la sentencia global no funcionaría y sería incapaz de cambiar su valor. Nos podemos imaginar que la sentencia global es como construir una puerta dentro de la función por la que acceder al programa principal, recoge la variable global, traérnosla al cuerpo de  de nuestra función, cerrar la puerta (para que no haya corriente), y modificar aquí a conveniencia el valor primitivo de la variable (o variables) global.



Pongamos nuestra atención en el detalle, importantísimo,  de que la modificación que hemos hecho a través de la sentencia global en el cuerpo de la función, produce un cambio efectivo del valor de la variable global, fuera del ámbito de la función: la variable global sigue manteniendo el mismo nombre pero su valor, independientemente de la ejecución de la función, pasa a ser ahora el que habíamos proporcionado previamente en la función. Hacemos hincapié en esto por su importancia. Insistimos: la llamada de una variable global dentro de una función y, por consiguiente, a un ámbito local, que es el del propio cuerpo de la función, y el valor de ésta se modifica, si devolvemos esta variable al exterior, es decir, al ámbito global mediante la variable global, el valor original que ésta tenía asignado se modifica y asume el nuevo: nuestra variable global declarada originalmente ya no vale lo mismo.
Una solución puede ser declarar una variable específica para el nuevo valor y asignarle la sentencia global, preservando de este modo en nuestro código el valor original que tenía la variable global original para usos posteriores dentro de otras funciones procediendo del mismo modo en todos los casos. En resumidas cuentas, constituye una buena práctica reservar la categorización como global para aquellas variables que almacenen valores que queramos usar como CONSTANTES, valores inalterables de principio a fin durante la lectura y ejecución de nuestro código (constantes físicas, químicas, matemáticas, gramaticales, económicas, lógicas, etc.), lo que minimizará la ocurrencia de errores, y que en caso de ser invocada al ámbito local de una función, y de obtener un nuevo valor que queramos trasladar al ámbito global, esto es, fuera de la función, asignarle la sentencia global declarando una variable nueva que le proporcione una referencia propia  que respete espacio de nombres (namespace).
Por esta razón, los programadores más expertos (senior) recomiendan no utilizar variables globales en nuestros programas o, por lo menos, no "alegremente", salvo que queramos utilizar constantes.

PANORÁMICA PARCIAL DEL MACIZO DE TENO, NOROESTE DE TENERIFE.

Con esto ponemos fin al apartado de las variables  globales y locales, nos tomamos algo para relajarnos, salimos a dar un paseo, escuchamos algo de música, una copita con los amigos, una partidita de lo que sea, y nos preparamos para la vuelta, donde comenzaremos a estudiar la estructura básica de una función definida por el usuario que, con lo que hemos visto hasta ahora, seguro que no nos va a resultar tan extraña.
No queremos terminar el capítulo sin dejar un pequeño apunte: a título mnemotécnico, podemos asumir la idea de que los parámetros representan, más o menos, variables (espacios de memoria) a la espera de recibir datos. Y estos datos se reciben (es decir, los parámetros se concretan en valores que llamamos argumentos) en el momento de llamar a la función en cualquier parte de nuestro programa.





VISTA DE ANAGA MIRANDO HACIA EL SUR, DESDE EL CASERÍO DE CATALANES. MACIZO DE ANAGA, NORESTE DE TENERIFE.


5 comentarios:

  1. Muchas gracias. Este es sin duda uno de los mejores blogs que me he encontrado para estudiar Python.

    ResponderEliminar
    Respuestas
    1. Muchísimas gracias, Andrés. Espero que este blog tuyo, vuestro y mío pueda ayudarte a conseguirlo. Te invito tanto a ti como a otros lectores de este blog a que hagan clic sobre la imagen del libro, bajo el epígrafe de Utilidades y Herramientas, en la barra lateral, arriba del todo, donde podrás encontrar un sitio web asociado a este mismo blog, con multitud de opciones interesantes (docenas de enlaces según categorías) que, sin duda, créeme, te ayudarán tanto a ti como a quienes lo consulten a mejorar y ampliar competencias con Python. Saludos.

      Eliminar
    2. Estoy de acuerdo.
      Este blog es una excelente aportación al aprendizaje de los que estamos empezando.
      Muchas gracias por compartir tanto conocimiento y tan bien llevando a cabo. Enhorabuena!

      Eliminar
    3. Este comentario ha sido eliminado por el autor.

      Eliminar
    4. Muchísimas gracias, José Luis. Un placer enorme compartir conocimientos de la mejor manera que podemos. Seguimos avanzando. Saludos.

      Eliminar