mayo 29, 2020
~ 7 MIN
Funciones en Python
< Blog RSSFunciones
Las funciones
es la principal herramienta que nos ofrece Python
a la hora de organizar nuestro código permitiendo también su reutilización. Como norma general, si te encuentras escribiendo el mismo código o una funcionalidad similar varias veces es muy probable que necesites un función
. Esta función
incluiría el código y cuando necesitamos ejectuarla la invocamos, enviando de manera opcional diferentes argumentos
y recuperando el resultado devuelto por la función
.
En Python
definimos una función con la palabra clave def
.
def func():
pass
⚠️ Utilizamos la palabra reservada
pass
para indicarle aPython
que no debe hacer nada.
Una vez tenemos nuestra función definida, podemos invocarla usando su nombre.
func()
De momento, nuestra función no lleva a cabo ninguna tarea interesante. Vamos a ver cómo podemos hacer que nuestra función imprima un string
por la consola.
def func():
print("hola")
func()
hola
Como puedes ver, incluímos todo el código que queremos ejecutar dentro de la función con la indentación correcta. Dentro de una función podemos definir nuevas variables que solo serán visibles dentro de la misma función.
def func():
s = "hola"
print(s)
func()
hola
# `s` sólo existe dentro de la función
s
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-10-ded5ba42480f> in <module>
----> 1 s
NameError: name 's' is not defined
Sin embargo, si que podemos utilizar variables definidas fuera de la función
.
s = "hola"
def func():
print(s)
func()
hola
# `s` ahora existe tanto dentro como fuera de la función
s
'hola'
Argumentos
En la mayoría de ocasiones queremos que nuestra función
lleve a cabo una acción determinada de manera abstracta, pudiendo nosotros especificar los datos sobre los que debería actuar. Para ello podemos definir argumentos
que la función
usará durante su ejecución.
def func(s):
print(s)
func("hola")
hola
func("hello")
hello
Ahora nuestra función
es mucho más versátil y reusable. Podemos definir diferentes arguments separados por una coma.
def func(a, b, c):
print(a, b, c)
func("hola", "que", "tal")
hola que tal
También podemos definir argumentos opcionales, de manera que si durante la llamada a la función no se especifica ningún valor se utilice el valor por defecto.
def func(a, b, c="hola"):
print(a, b, c)
func("hola", "que")
hola que hola
⚠️ Recuerda siempre definir los
argumentos
obligatorios primero, y luego los opcionales.
Es posible utilizar el nombre del argumento cuando llamamos a una función
.
func(a=1, b=2)
1 2 hola
Devolviendo valores
A parte de recibir argumentos
una función puede devolver resultados. Para ello utilizamos la palabra reservada return
.
def func(a, b):
return a + b
x = func(1, 2)
x
3
Python
nos permite devolver múltiples valores a la vez
def func(a, b):
return a + b, a -b
x1, x2 = func(1, 2)
x1, x2
(3, -1)
Funciones anónimas
Una función anónima
, también conocidas como funciones lambda
, es una forma de definir funciones que sólo contienen una declaración, el resultado de la cual es automáticamente devuelto.
def func(x):
return 2*x
# misma función, pero definida como lambda function
func = lambda x: 2*x
func(2)
4
En el análisis de datos es bastante común tener que llevar a cabo transformaciones que consisten en una sola declaración, y ser capaces de expresar esta funcionalidad de manera concisa aumenta nuestra productividad a la vez que reduce las posibles fuentes de error. Podemos definir funciones anónimas con varios parámetros.
func = lambda x, y: x + y
func(1, 2)
3
Generadores
Python
implementa una manera consistente de iterar sobre secuencias, lo cual podemos utilizar en nuestro favor para definir nuestra propia lógica de iteración mediante un tipo de funciones llamadas generadores
. Podemos definir un generador
de la misma manera que definimos una función
normal, cambiando la palabra return
por yield
. Esto le indicará a Python
que pese a que la función ha devuelto un valor, la ejecución de la función no ha terminado y esperará que siga devolviendo valores en el futuro.
# itera una lista de números devolviendo sólo números impares
def func(n=10):
for i in range(n):
if i % 2:
yield i
gen = func()
# ahora gen es un objeto `iterable`
for i in gen:
print(i)
1
3
5
7
9
Una manera alternativa de definir un generador de manera más compacta es la siguiente
# similar a `list comprehension`
gen = (i for i in range(10) if i % 2)
for i in gen:
print(i)
1
3
5
7
9
En análisis de datos es muy común iterar sobre grandes conjuntos de datos, aplicando transformaciones o procesados, y dependiendo del algoritmo que utilicemos querremos iterar nuestros datos de una manera u otra (por ejemplo de uno en uno, en grupos o batches
, aplicando alguna transformación, etc). Un generador
nos será muy útil en estas ocasiones.
Manejando errores
Si has trabajado un poco con Python
, o simplemente has seguido los ejemplos que hemos ido viendo, te habrás dado cuenta que en ocasiones Python
no devuelve un mensaje de error cuando algo va mal. Con el objetivo de hacer nuestro código robusto, tenemos que ser capaces de anteponernos a estas situaciones evitando que un programa se "cuelgue" o proveiendo mensajes de error útiles. Por ejemplo, la siguiente función espera un argumento y devuelve el mismo valor transformado en un número entero. Esto es posible para algunos tipos de datos, pero otros (como los string
) no se pueden convertir a números y nuestra función devolverá un error.
def func(x):
return int(x)
func(1)
1
func("hola")
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-63-3db432dc1599> in <module>
----> 1 func("hola")
<ipython-input-60-185a22a41eef> in func(x)
1 def func(x):
----> 2 return int(x)
ValueError: invalid literal for int() with base 10: 'hola'
Podemos anteponernos a este problema y proveer de un mejor mensaje de error con un bloque try-except
. Nuestra función intentará ejecutar el código dentro del bloque try
y, de recibir un error, saltará al bloque except
.
def func(x):
try:
return int(x)
except:
print("el argumento no se puede convertir a int")
func(1)
1
func("hola")
el argumento no se puede convertir a int
En ocasiones podemos querer ejecutar código tanto si hay errores como si no, para ello podemos usar el bloque finally
.
def func(x):
try:
return int(x)
except:
print("el argumento no se puede convertir a int")
finally:
print("siempre")
func(1)
siempre
1
func("hola")
el argumento no se puede convertir a int
siempre
Resumen
Con el objetivo de hacer nuestro código más robusto, legible y reutilizable Python
nos ofrece la opción de utilizar funciones
. En este post hemos visto cómo definir tales funciones, trozos de código que podemos invocar en cualquier momento enviándo argumentos
y devolviendo resultados. Hemos hablado también sobre funciones anónimas, una manera de definir funciones cortas con una sola declaración muy útiles cuando necesitamos una función de manera inmediata. El uso de generadores nos permite defininr nuestros propios objetos iterables, útiles en el análisis de datos para iterar sobre grandes cantidades de datos llevando a cabo alguna lógica en concreto. Por último hemos visto como manejar los diferentes errores que pueden surgir en la invocación de función con el objetivo de evitar que nuestro programa se quede "colgado" cuando algún error surge en su ejecución.