Tema 10. Funciones

Explicación y conceptos básicos de la definición de Funciones en Python 3.

Función

Función. Una función en Python es una pieza de código reutilizable que solo se ejecuta cuando es llamada.

Se define usando la palabra reservada def y estructura general es la siguiente:

def nombre_funcion(input1, input2, ..., inputn):
  cuerpo de la función
  return output

Observación. La instrucción return finaliza la ejecución de la función y devuelve el resultado que se indica a continuación. Si no se indicase nada, la función finalizaría, pero no retornaría nada.

Como hemos visto, en general, las funciones constan de 3 partes:

  • Inputs (parámetros o argumentos). Son los valores que le pasamos como entrada a la función.

  • Cuerpo. Son todas las operaciones que lleva a cabo la función.

  • Output. Es el resultado que devuelve la función.

Observación. Los parámetros son variables internas de la función. Si probásemos a ejecutar una de dichas variables en el entorno global, nos saltaría un error. Este tema lo trataremos más en detalle más adelante.

Con lo visto anteriormente, a la hora de construir una función hau que hacerse las siguientes preguntas:

  • ¿Qué datos necesita conocer la función? (inputs)

  • ¿Qué hace la función? (cuerpo)

  • ¿Qué devuelve? (output)

Observación. Los inputs y el output son opcionales: podemos definir una función sin necesidad de proporcionarle inputs y sin que nos devuelva nada.

Una vez definida una función, para llamarla utilizamos su nombre seguido de paréntesis:

Hemos dicho que tanto los inputs como el output son opcionales. Veamos algunos ejemplos con diferentes casos.

Ejemplo 1

Veamos otro ejemplo que no necesite ningún parámetro y no nos devuelva nada, tal y como ocurría con my_first_function()

Nuestra función hello_world(), cuando es llamada, imprime "Hola mundo!", pero no devuelve absolutamente nada.

Ejemplo 2

Veamos un ejemplo de función que no necesita ningún input, pero que devuelve un output. Por ejemplo, una función que nos devuelve "¡Buenos días!"

Ya hemos declarado la función. Si la llamamos, obtenemos el siguiente resultado:

Como nos devuelve el saludo, lo podemos guardar en una variable, que será de tipo string

Ejemplo 3

Veamos ahora un ejemplo de función que no nos devuelva nada, pero que sí toma algún parámetro

Ahora nuestra función good_morning() recibe como parámetro un nombre y nos muestra por pantalla un "¡Buenos días!" junto con el nombre indicado.

Ejemplo 4

Por último, vamos a crear una función que nos calcule la división entera de dos números y nos retorne el cociente y el resto.

Esta función, a la que hemos llamado euclidean_division, calcula el cociente y el resto de dos números cualesquiera y devuelve como resultado esos dos números calculados.

Utilicemos ahora nuestra función para calcular el cociente y el resto de la división 41÷ 7 euclidean_division(x = 41, y = 7)

Al llamar a la función e indicarle por parámetros x = 41 e y = 7, hemos obtenido como resultado la tupla (5, 6). El significado de dicho resultado es que el cociente entero de 41 ÷ 7 es 5, mientras que el resto es 6. Efectivamente:

41=75+641 = 7\cdot 5 + 6

También podríamos guardar en variables diferentes los resultados que nos devuelve nuestra función, para poder trabajar con ellos en el entorno global

EJERCICIO 1:

Vamos a crear una función que cuente y devuelva todas las apariciones de una letra proporcionada por parámetro en una frase también proporcionada por parámetro.

Parámetros

Por defecto, una función debe ser llamada con el número correcto de argumentos. Esto es, si la función espera 2 argumentos, tenemos que llamar a la función con esos 2 argumentos. Ni más, ni menos.

Si intentamos llamar a la función complete_name() pasando 1 solo parámetro o 3 parámetros, entonces la función devuelve error.

Número arbitrario de argumentos

Si no sabemos el número de parámetros que van a ser introducidos, entonces añadimos un asterisco * previo al nombre del parámetro en la definición de la función. Los valores introducidos serán guardados en una tupla.

Número arbitrario de claves de argumento

Hasta ahora hemos visto que al pasar valores por parámetro a la función, podemos hacerlo con la sintaxis clave_argumento = valor o directamente pasar por parámetro el valor siguiendo el orden posicional de la definición de la función:

En realidad, los nombres completos pueden tener dos o incluso más apellidos, pero no sabemos si el usuario tiene 1 o 2 o más. Entones, podemos añadir dos asteriscos ** antes del nombre del parámetro para así poder introducir tantos como queramos sin que salte error

Parámetros por defecto

Hemos visto que una función en Python puede tener o no parámetros.

En caso de tener, podemos indicar que alguno tenga un valor por defecto.

La función diff() calcula la diferencia entre los dos números que introducimos por parámetros. Podemos hacer que el sustraendo por defecto valga 1 del siguiente modo:

Si ahora llamamos a la función indicando únicamente el valor del parámetro x, ocurre lo siguiente:

Como resultado hemos obtenido 19, ya que el valor que ha tomado el parámetro y ha sido el que le hemos dicho que tome por defecto, es decir, $y = 1$ y, consecuentemente, $x-y = 20-1 = 19$

EJERCICIO 2

Vamos a crear una función que muestre por pantalla el triángulo de Pascal. Por parámetro se indicará el número de filas a mostrar. Por defecto n valdrá 1.

Para calcular el número combinatorio en Python, utilizaremos el siguiente método:

Docstring

Docstring. Son comentarios explicativos que ayudan a comprender el funcionamiento de una función.

  • Van entre triple comilla doble.

  • Pueden ser multilínea.

  • Se sitúan al principio de la definición de la función.

Retomando el ejemplo de la división entera, podríamos utilizar docstring del siguiente modo:

Con la ayuda del método .__doc__ podemos acceder directamente a la información indicada en el docstring de una función

Variables de una función

Dentro de una función en Python existen dos tipos de variables:

  • Variable local. Aquella que es creada y solamente existe dentro de la función.

  • Variable global. Aquella que es creada en el entorno global.

Dada la siguiente función:

Si nosotros queremos imprimir por ejemplo el valor que toma la variable prod en el entorno global nos saltará un error, pues esta variable no existe a nivel global porque no ha sido declarada en dicho entorno ya que solamente ha sido declarada a nivel local, dentro de la función arithmetic_operations().

Si se diese el caso de que sí hubiese sido definida la variable prod en el entorno global, como lo que ocurre en el siguiente bloque de código, por mucho que la variable local tenga el mimso nombre y por mucho que ejecutemos la función, el valor de la variable global no se ve modificado

Si dentro de una función utilizamos la palabra reservada global a una variable local, ésta automáticamente pasa a ser una variable global previamente definida.

Veamos un ejemplo de función que nos devuelve el siguiente número del entero n definido en el entorno global:

Paso por copia vs. paso por referencia

Dependiendo del tipo de dato que pasemos por parámetro a la función, podemos diferenciar entre

  • Paso por copia. Se crea una copia local de la variable dentro de la función.

  • Paso por referencia. Se maneja directamente la variable y los cambios realizados dentro de la función afectan también a nivel global.

En general, los tipos de datos básicos como enteros, en coma flotante, strings o booleanos se pasan por copia, mientras que estructuras de datos como listas, diccionarios, conjuntos o tuplas u otros objetos se pasan por referencia.

Un ejemplo de paso por copia sería:

Un ejemplo de paso por referencia sería:

Para evitar la modificación de la lista original, podemos hacerlo introduciendo por parámetro una copia de dicha lista

Funciones más complejas

Las funciones pueden ser más completas, pues admiten tanto operadores de decisión como de iteración.

Volviendo al ejemplo 4, la función creada claramente es muy sencilla, pues suponemos que el usuario va a introducir por parámetros números enteros.

Ejercicio.

Mejora la función euclidean_division() para que

  • Compruebe que los números introducidos son enteros. En caso de no ser así, indicar que se ha tomado la parte entera de los valores introducidos.

  • Realice la división entera del mayor parámetro (en valor absoluto) entre el menor parámetro. Esto es, si el usuario introduce x = -2 e y = -10, como 10 > 2, entonces la función debe llevar a cabo la división entera de -10 entre -2.

  • Imprima por pantalla una frase indicando la división realizada y el cociente y el resto obtenidos.

  • Devuelva el cociente y el resto a modo de tupla

Ejemplo 5

Veamos una función que dado un número, nos dice si éste es positivo, negativo o vale 0.

Ejemplo 6

Veamos ahora una función que contiene un bucle for y que dado un número entero, nos imprime su tabla de multiplicar con sus 10 primeros múltiplos y nos devuelve una lista con todos esos múltiplos:

Vamos ahora a mejorar la función multiplication_table10() para que si el usuario decide introducir un número que no sea entero, nuestra función le avise y le explique el error que está cometiendo:

Ejemplo 7

Creemos ahora una función que dada una frase acabada en punto, nos devuelva si contiene o no la letra "a" haciendo uso de un bucle while

Ejercicio.

Generalizad la función contains_a() a una función llamada contains_letter() que devuelva si una frase cualquiera (no necesariamente acabada en punto) contiene o no la letra indicada también por el usuario. Tenéis que hacerlo únicamente con operadores de decisión e iteración. No vale usar ningún método existente de string.

Funciones recursivas

Función recursiva. Es una función que se llama a sí misma.

¡Cuidado! Hay que tener mucho cuidado con este tipo de funciones porque podemos caer en un bucle infinito. Es decir, que la función no acabara nunca de ejecutarse.

Una función recursiva que entraría en bucle infinito sería la siguiente.

¿Por qué decimos que entra en bulce infinito? Pues porque solo parará si nosotros interrumpimos la ejecución.

Esto se debe a que no le hemos indicado un caso de parada a la función, denominado caso final.

Caso final. Es el caso que indica cuándo debe romperse la recursión. Hay que indicarlo siempre para no caer en un bucle infinito.

En el caso de la función powers(), podemos indicar como caso final cuando el valor resultante supere 1000000. Lo indicamos con un if

EJERCICIO 3:

Vamos a crear una función recursiva que lleve a cabo una cuenta atrás.

EJERCICIO 4:

Vamos a crear una función recursiva que calcule el factorial de un número entero positivo.

Ejemplo 8

Veamos ahora un ejemplo clásico de función recursiva que funciona correctamente.

Queremos una función que nos imprima el término i-ésimo de la sucesión de Fibonacci. Es decir, nosotros le indicamos el índice del término y la función nos devuelve el valor de dicho término.

La sucesión de Fibonacci es

1,1,2,3,5,8,13,1, 1, 2, 3, 5, 8, 13,\dots

Es decir, cada término se obtiene de la suma de los dos anteriores.

F0=F1=1F_0 = F_1 = 1 Fn=Fn1+Fn2,n2F_n = F_{n-1} + F_{n-2}, n\geq 2

Con lo cual, la función que queremos y a la que hemos llamado Fibonacci() es:

Como veis, le hemos indicado a la función cuando parar. Esto es, el caso final resulta ser cuando el índice vale 0, pues no existen índices negativos.

EJERCICIO 5:

Vamos a crear una función que resuelva ecuaciones de primer grado de la forma Ax+B=0 siempre que A≠0 .

Funciones helper

Al igual que las funciones pueden llamarse a sí mismas, también pueden llamar a otras funciones.

Función helper. Es una función cuyo propósito es evitar la repetición de código.

Si nos dan la siguiente función

Vemos que el print se repite salvo por la última palabra.

Podríamos pensar en crear la función helper siguiente:

Si utilizamos la función helper, la función sign_sum() quedaría modificada del siguiente modo:

Con lo cual ya no hay código repetido.

Y como se puede observar, la función original funciona correctamente.

EJERCICIO 5:

Vamos a crear una función que haga de calculadora (suma, resta, producto y división), haciendo uso de funciones helper para mostrar la operación realizada. Pediremos por parámetro tipo de operación ("sum", "subract", "product", "division") y dos números reales. Devolveremos el resultado de la operación correspondiente.

REPASO

Last updated