Funciones#
Motivación#
Para evaluar el polinomio \(f(x) = 2 x^2 - 5x + 2\) en \(x=2\), habíamos escrito el siguiente código:
x = 2
2 * x**2 - 5 * x + 2
0
Al parametrizarlo con la variable x
,
es más fácil cambiar el punto donde evaluamos el polinomio.
ya que hay una única fuente para su valor:
la linea que declara x=2
.
Pero, ¿cómo podemos evaluar el polinomio en más de un punto?
Una opción es copiar y pegar el código dos veces:
x = 2
y2 = 2 * x**2 - 5 * x + 2
x = 3
y3 = 2 * x**2 - 5 * x + 2
y2, y3
(0, 5)
donde guardamos cada valor en una variable distinta
(y2
para x=2
e y3
para x=3
).
El problema con esta solución es similar
al que teníamos cuando no usabamos la variable x
:
no hay una única fuente para el polinomio.
Por ejemplo,
si queremos cambiar el polinomio por uno de otro grado,
tenemos que editar todas las lineas donde dice:
... = 2 * x**2 - 5 * x + 2
.
Entonces,
queremos definir en un único lugar el polinomio,
y después poder evaluarlo en distintos x
.
Para eso,
necesitamos poder definir una variable x
«indeterminada»,
es decir,
que aún no tome un valor concreto.
Esto se puede lograr definiendo una función.
Definiendo una función#
La sintaxis para definir una función es la siguiente:
def duplicar(n):
y = 2 * n
return y
donde definimos una función llamada duplicar
,
con un parámetro n
.
La primera linea de la función se compone de:
def
, una palabra clave para que Python sepa que estamos definiendo una función,duplicar
, que es el nombre de la variable donde se va a guardar esta función,(n)
, donde ponemos el nombre de la «variable indeterminada» o parámetro que toma la función,:
, al final, para indicar que comienza el código de la función.
En las siguientes lineas, está el bloque de código que se va a ejecutar cuando se corra la función.
Para que Python pueda distinguir que código pertenece a la función,
el código debe estar «indentado», tabulado,
o con sangría, respecto a la palabra clave def
.
La convención es usar 4 espacios, pero cualquier número de espacios funciona (mientras sea la misma cantidad en un mismo bloque).
Finalmente,
está el return
,
que es otra palabra clave
y será el resultado que nos «devolverá» función al ejecutarla.
Llamando una función#
Ya definimos la función duplicar(n)
.
Pero,
si tratamos de acceder a la variable n
,
nos dice que aún no existe:
n
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[4], line 1
----> 1 n
NameError: name 'n' is not defined
Y,
por lo tanto,
tampoco existe y
.
Solo existe la «receta»
que está guardada en la variable duplicar
:
duplicar
<function __main__.duplicar(n)>
Para usar la receta, necesitamos llamarla (del inglés, call) pasándole los parámetros, que se hace de la siguiente manera:
z = duplicar(n=3)
z
6
donde guardamos el resultado en la variable z
.
La linea z = duplicar(n=3)
es equivalente* a haber ejecutado el siguiente código:
n = 3
y = 2 * n
z = y
del n
del y
*aunque no exactamente igual.
Es decir,
ni n
,
el parámetro,
ni y
,
una variable intermedia de la función,
existen después de ejecutarse duplicar(n=3)
:
n
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[7], line 1
----> 1 n
NameError: name 'n' is not defined
Ambas,
n
e y
,
son variables internas a la función.
Al correr esta linea,
z = duplicar(n=3)
Python corre el código de duplicar
en una hoja borrador,
y después reemplaza el llamado,
duplicar(n=3)
,
por su resultado,
6
:
z = 6
Al llamar una función, también se le puede pasar una variable, que no tiene porque coincidir en nombre con el parámetro:
mi_numero = 21
duplicar(n=mi_numero)
42
Y tampoco hace falta especificar el nombre del parámetro:
duplicar(mi_numero)
42
aunque a veces hace el código más legible. Más adelante veremos mas ejemplos.
Ejercicio 1#
Definir una función que tome un parámetro x
y devuelva el resultado de evaluar el polinomio \(f(x) = 2 x^2 - 8x + 6\).
Evaluar en \(x=2\) y \(x=3\).
# Escriba su solución aquí
Solución#
Show code cell content
def mi_polinomio(x):
return 2 * x**2 - 8 * x + 6
y2 = mi_polinomio(2)
y3 = mi_polinomio(3)
y2, y3
(-2, 0)
Noten que, dentro de la función, no hace falta guardar el resultado en una variable intermedia.
Se puede poner directamente return
de una expresión.
Múltiples parámetros#
Para definir una función con múltiples parámetros,
estos se separan con comas (,
).
Por ejemplo:
def dividir(x, y):
return x / y
dividir(8, 4)
2.0
Llamando funciones de múltiples parámetros#
Vimos que hay dos formas de llamar a las funciones:
por posición
por nombre o palabra clave (keyword, en inglés).
Por posición, se asignan los valores en el mismo orden en el que se definió en la función:
dividir(8, 4)
2.0
Es decir,
para dicha ejecución de dividir
,
x=8
e y=4
.
Cuando se pasa por nombre, se puede cambiar el orden de los parámetros:
dividir(y=4, x=8)
2.0
También, se pueden combinar ambas formas:
dividir(8, y=4)
2.0
Pero una vez que pasamos un parámetro por nombre, es decir, no posicionalmente, todos los parámetros restantes también tienen que ser por nombre:
dividir(x=2, 3)
Cell In[16], line 1
dividir(x=2, 3)
^
SyntaxError: positional argument follows keyword argument
El error nos dice que un argumento posicional sigue a un argumento por palabra clave (keyword).
Para esta función dividir(x, y)
,
es confuso cambiar el orden de los parámetros llamándolos por nombre.
Si hubiésemos definido dividir(numerador, denominador)
,
sería un poco menos confuso hacer dividir(denominador=2, numerador=8)
.
Ejercicio 2#
Escribir una función que permita evaluar un polinomio cuadrático:
y usarla para evaluar el polinomio \(f(x) = 2 x^2 - 8x + 6\) en \(x = 2\).
Usar la cantidad de parámetros que hagan falta.
# Escriba su solución aquí
Solución#
Show code cell content
def evaluar_cuadratica(x, a, b, c):
return a * x**2 + b * x + c
evaluar_cuadratica(2, a=2, b=-8, c=6)
-2
Noten que usar keyword arguments permite leer más fácilmente cuáles son los coeficiente del polinomio y cuál el valor donde lo evaluamos.
Si escribimos evaluar_cuadratica(2, 2, -8, 6)
,
llegamos al mismo resultado.
Pero necesitaríamos ir a ver la definición de la función
para saber si x
es el primero o el último de los argumentos.
Múltiples return
#
Una función solo puede devolver (return
) una única variable. Pero eso no significa que no pueda haber más de un return
dentro de una función.
La función se ejecuta secuencialmente,
linea por linea,
y termina apenas encuentre el primer return
.
Por ejemplo:
def triplicar(x):
z = 3 * x
return z
w = x / 0
return w
triplicar(3)
9
¿Cómo sabemos que no corrió el resto?
Si se hubiese ejecutado w = x / 0
, se habría producido un error.
Pongámoslo antes del return
:
def triplicar(x):
z = 3 * x
w = x / 0
return z
return w
triplicar(3)
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Cell In[20], line 8
4 return z
5 return w
----> 8 triplicar(3)
Cell In[20], line 3, in triplicar(x)
1 def triplicar(x):
2 z = 3 * x
----> 3 w = x / 0
4 return z
5 return w
ZeroDivisionError: division by zero
Veremos más adelante que puede ser útil tener más de un return
dentro de una función.
Múltiples resultados#
Si queremos devolver múltiples resultados,
una forma es separarlos con comas (,
) en el return
:
def sumar_y_restar(x, y):
suma = x + y
resta = x - y
return suma, resta
z = sumar_y_restar(5, 3)
z
(8, 2)
Lo que estamos haciendo es crear una «tupla», que veremos más adelante.
Podemos «desempacar» el resultado asignándolo a variables separadas por comas:
a, b = sumar_y_restar(5, 3)
a
8
donde a
y b
guardaron suma
y resta
,
respectivamente.
Ojo, si no usamos la misma cantidad de variables, nos va a devolver el siguiente error:
a, b, c = sumar_y_restar(5, 3)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[23], line 1
----> 1 a, b, c = sumar_y_restar(5, 3)
ValueError: not enough values to unpack (expected 3, got 2)
Si leen el error,
dice que no pudo desempacar los valores.
Esperaba 3 valores (a, b, c
),
pero la función solo devolvió 2.
Ejercicio 3#
Definir una función para calcular las raíces de una cuadrática, y calcule las raíces del polinomio \(f(x) = 2 x^2 - 5x + 2\).
# Escriba su solución aquí
Solución#
Show code cell content
def raices_cuadratica(a, b, c):
disc = (b**2 - 4 * a * c) ** (1 / 2)
raiz_1 = (-b - disc) / (2 * a)
raiz_2 = (-b + disc) / (2 * a)
return raiz_1, raiz_2
raices_cuadratica(2, -5, 2)
(0.5, 2.0)
Recordemos los beneficios de encapsular código en una función:
Permite ejecutar fácilmente una receta para diferentes parámetros (
a
,b
,c
).Si queremos mejorar la receta, o encontramos un error en esta, solo tenemos que editar código en un lugar.
Elimina las variables intermedias, que solo son útiles dentro de la receta (
disc
).Es más fácil de leer el código. Comparen
disc = (b**2 - 4 * a * c) ** (1 / 2) x_1 = (-b - disc) / (2 * a) x_2 = (-b + disc) / (2 * a)
contra
x1, x2 = raices_cuadratica(a, b, c)
En el primer caso, tenemos que entender que está haciendo el código para saber que son
x_1
yx_2
.En el segundo caso, está explicito en el nombre de la función.
Probando la función#
Es importante probar la función después de definirla, para estar seguros que la definimos bien.
Hay, al menos, dos formas:
Probar casos conocidos#
Una forma es probar casos donde conozcamos la respuesta.
Por ejemplo:
Para los polinomios de la forma \(x^2 + c\), las raíces son \(\pm \sqrt{-c}\):
raices_cuadratica(1, 0, -4)
(-2.0, 2.0)
Para polinomios de la forma \(x^2 + bx = x(x + b)\), las raíces son \(0\) y \(-b\):
raices_cuadratica(1, 2, 0)
(-2.0, 0.0)
Evaluar propiedades#
Otra forma es evaluar propiedades de la solución.
Por definición, si evaluamos el polinomio en las raíces, tiene que dar 0.
Entonces, podemos aprovechar la otra función que definimos, y probar para un polinomio más general:
a, b, c = 1, 5, -10
raiz_1, raiz_2 = raices_cuadratica(a, b, c)
y1 = evaluar_cuadratica(raiz_1, a, b, c)
y2 = evaluar_cuadratica(raiz_2, a, b, c)
y1, y2
(0.0, -1.7763568394002505e-15)
Nota:
y2
da un número distinto,
pero muy cercano a 0.
Esto se debe a errores de redondeo,
ya que la computadora tiene una precisión finita.