En este video tutorial seguiremos avanzando con el tema de las funciones. Primero, veamos qué es el flujo de ejecución de un programa. El flujo de ejecución de un programa es el orden en el que las sentencias se ejecutan. Sin considerar las funciones, el flujo de ejecución comienza con la primera sentencia del programa, y las sentencias se ejecutan de arriba hacia abajo. En caso de haber un condicional, se saltean las líneas de código que no cumplen con la condición. Al considerar las funciones hay que tener en cuenta los siguientes puntos sobre el flujo de ejecución. Primero, las definiciones de función no alteran el flujo de ejecución del programa, pero las sentencias que están dentro de las funciones no se ejecutan hasta que éstas sean llamadas. Segundo, la función debe definirse antes de utilizarse. Tercero, las llamadas a función son como un desvío en el flujo de ejecución; en lugar de continuar con la siguiente sentencia, el flujo salta a la primera sentencia de la función llamada, ejecuta todas las sentencias de la función y regresa para continuar donde estaba previamente. Además, dentro de las sentencias de una función, se puede llamar a otra función y, al finalizar, se irá ejecutando el código de la función que hizo el llamado. Cada vez que una función termina, el programa regresa al punto de la función desde donde fue llamada la función que recién terminó. Cuando llega el fin del programa, la ejecución termina. Entonces, una consideración a tener en cuenta es que, al leer un programa, no hay que leerlo de arriba hacia abajo, sino que hay que seguir el flujo de ejecución. Veamos un ejemplo para comprender mejor cómo es el flujo de ejecución de un programa. Abro el archivo "máximo.py", donde se definen dos funciones: una, que devuelve el máximo entre dos números, y otra, que devuelve el máximo entre tres números. Al leer el programa, Python comienza desde la primera línea. En este caso, indica con qué codificación leer el archivo. Después, sigue la definición de la función "máximo_2", donde Python la leerá y la cargará en el contexto del programa, pero no ejecutará su código. Lo mismo pasa cuando lee la definición de la función "máximo_3". Después, en la línea 14, hay un "print" de la función "máximo_2" pasándole los argumentos "2" y "5". En este caso, el intérprete de Python se posicionará en la primera línea de la función, con la variable "a" asignada en "2" y la variable "b" asignada en "5". Vimos que los argumentos son variables locales al contexto de la función. Luego, se empieza a ejecutar el código de la función de arriba hacia abajo. La primera línea corresponde al "docstring", con lo cual el intérprete no hace nada. Luego, se asigna la variable "máximo" con el valor de "a". Después, se pregunta si "b" es mayor que "a". En ese caso, se cambia el valor de la variable "máximo", asignándole el valor de "b". Por último, se devuelve el valor de la variable "máximo". Al finalizar la función "máximo_2", la misma retorna con el valor "5". Ese valor es el que recibirá la función "print", con lo cual, es el valor que se imprimirá en pantalla. Con la siguiente línea, la línea 15, pasa algo similar. Se llama a "máximo_3", con los valores "6", "1" y "5". Al ejecutar esta función, se hacen dos llamadas a la función "máximo_2". Primero, se llama a la función "máximo_2" con "b" y "c", es decir, con los valores "1" y "5", la cual devuelve el valor "5"; y luego, se llama a la función "máximo_2" con los valores "6" y "5". La función retornará el valor "6", y este valor es el que devolverá la función "máximo_3", que luego recibirá el "print" para imprimirlo en pantalla. Ahora veamos un poco más en detalle las formas de definir y pasar los parámetros a una función. Abro el archivo "funciones.py". La función "peso" recibe los parámetros "masa" y "gravedad". En el caso del parámetro "gravedad", tiene un valor por defecto, con lo cual, puedo llamar a la función pasándole el parámetro, o no. Por ejemplo, puedo la función "peso" con el valor "10", el cual se le asignará al argumento "masa", y al no enviar argumento "gravedad", se tomará el valor por defecto; o sino, puedo enviar los dos argumentos, como ocurre en este segundo caso, donde la masa será 10 y la gravedad 1.63. Notar que los argumentos se envían en orden. Por eso, estos argumentos se llaman "posicionales". También puedo pasarle uno o más argumentos de palabra clave o argumentos nombrados. En este caso, no hace falta respetar el orden, ya que uno se lo está asignando. Es importante saber que, después de haber puesto un argumento nombrado, no puedo poner uno sin nombre. En ese caso, dará un "SyntaxError". Al revés sí se puede, como se puede ver en este caso. Por último, también se puede recibir una lista de parámetros, como se puede ver en este caso. Se indica con un asterisco antes el nombre del parámetro, entonces, puedo pasarle a la función todos los parámetros que quiera. Hay dos variantes: pasarlos empaquetados o desempaquetados. Lo mismo que se puede recibir un diccionario con parámetros, como se puede ver en este caso, se indica con dos asteriscos ante el nombre del parámetro. Más adelante veremos más sobre el uso de listas y diccionarios. Lo importante ahora es saber que existe la posibilidad de recibir una lista de parámetros arbitraria y un diccionario con los parámetros nombrados. Hemos visto con más detalle las definiciones y usos de funciones en Python.