Python: Funciones
def nombre_funcion(parametro1[=valor_por_defecto], ...): bloque_de_código nombre_funcion([parametro1=]valor_parametro_1, ...)
Para definir la función usamos def
seguido de un nombre y, opcional, entre paréntesis los parámetros que recibe la función. Cada parámetro podemos especificar (opcional también) y valor por defecto, con lo cual si al llamar a la función no se pone ese parámetro se toma el valor indicado por defecto. Si no indicamos un valor por defecto y no ponemos un valor al llamar a la función se producirá una excepción TypeError
.
Cuando llamamos a la función debemos poner los parámetros en el mismo orden que se han definido a menos que indiquemos el nombre del parámetro, que podremos poner el orden que queramos.
Una función siempre devuelve un valor, si no indicamos el comando return
devuelve por defecto None
.
def conecta(router, ruser='admin, rpass): print('Conectando al router {} con usuario {} y contraseña {}...'.format(router, ruser, rpass)) conecta('192.168.1.1', 'admin', 'qwerty') Conectando al router 192.168.1.1 con usuario admin y contraseña qwerty... conecta(rpass='qwerty', ruser='admin',router='192.168.1.1') Conectando al router 192.168.1.1 con usuario admin y contraseña qwerty... conecta('192.168.1.1',rpass='qwerty', ruser='admin') Conectando al router 192.168.1.1 con usuario admin y contraseña qwerty...
Cuando ponemos valores por defecto en la definición, a partir del parámetro que se ponga el valor por defecto, el resto obligatoriamente deben tener valor por defecto, si no, se produce un error de sintaxis:
def conecta(router, ruser='admin', rpass): print('Conectando al router {} con usuario {} y contraseña {}...'.format(router, ruser, rpass)) File "<ipython-input-4-89827ff3f1f2>", line 1 def conecta(router, ruser='admin', rpass): ^ SyntaxError: non-default argument follows default argument
este es un ejemplo correcto:
def conecta(router, ruser='admin', rpass='qwerty'): print('Conectando al router {} con usuario {} y contraseña {}...'.format(router, ruser, rpass)) conecta('192.168.1.1') Conectando al router 192.168.1.1 con usuario admin y contraseña qwerty... conecta('192.168.1.1', 'operador') Conectando al router 192.168.1.1 con usuario operador y contraseña qwerty...
Los parámetros de tipo enteros, flotantes, cadenas y lógicos se pasan por valor con lo que se crea una copia local de la variable dentro de la función.
def potencia(numero): numero **= numero print(numero) numero = 10 potencia(numero) 10000000000 numero 10
Los parámetros de listas, conjuntos, tuplas y diccionarios que se pasan como referencia con lo que se maneja directamente la variable, los cambios realizados dentro de la función le afectarán también fuera.
def potencia(numeros): for i,n in enumerate(numeros): numeros[i] **= numeros[i] print(numeros[i]) numeros = [2, 9, 10] potencia(numeros) 4 387420489 10000000000 numeros [4, 387420489, 10000000000]
Si queremos pasar una lista, conjunto, tupla o diccionario como valor, lo que podemos hacer es una copia en otra variable de forma temporal o pasar al vuelo el valor del elemento usando slice:
def potencia(numeros):
for i,n in enumerate(numeros):
numeros[i] **= numeros[i]
print(numeros[i])
numeros = [2, 9, 10]
potencia(numeros[:]) <-- pasamos copia
4
387420489
10000000000
numeros
[2, 9, 10]
Hay que tener cuidado con parámetros modificables (mutables) ya que se hace referencia al objeto principal y no una copia local, con un ejemplo se ve mejor:
def funcion(otra_lista): print(otra_lista) otra_lista.append('nuevo valor') mi_lista = ['valor viejo'] funcion(mi_lista) ['valor viejo'] mi_lista ['valor viejo', 'nuevo valor']
Lo que pasa aquí es que tanto mi_lista
como otra_lista
apuntan al mismo contenido, con lo cual se modifica lo mismo se use el nombre que se use.
Podemos llamar a una función en vez de usar parámetros individuales, tomando esos valores desde una lista o un diccionario.
def conecta(router, ruser='admin', rpass='qwerty'): print('Conectando al router {} con usuario {} y contraseña {}...'.format(router, ruser, rpass)) lista = ['192.168.1.1', 'admin', 'qwerty'] conecta(*lista) Conectando al router 192.168.1.1 con usuario admin y contraseña qwerty...
En el caso de las listas debemos preceder el nombre de la lista con un *
y la lista debe contener el número exacto de elementos como parámetros pida la función, o menos si se han usado valores por defecto, si no se produce una excepción TypeError
.
def conecta(router, ruser='admin', rpass='qwerty'): print('Conectando al router {} con usuario {} y contraseña {}...'.format(router, ruser, rpass)) diccionario = { 'router': '192.168.1.1', 'ruser': 'operador1', 'rpass': 'op_pass', } conecta(**diccionario) Conectando al router 192.168.1.1 con usuario operador1 y contraseña op_pass...
En el caso de los diccionarios debemos preceder el nombre con doble **
y el diccionario debe contener el número exacto de elementos como parámetros pida la función, o menos si se han usado valores por defecto, si no se produce una excepción TypeError
.
Podemos crear funciones que acepten un número indeterminado de parámetros, sin tener que definir cuales son, pueden ser de dos tipos, por posición (listas) o por nombre (diccionarios).
Por posición, los argumentos son una lista, veamos un ejemplo que crea una cadena unida por puntos:
def unir_con_puntos(*args, sep="."): return sep.join(args) unir_con_puntos("hola", "dos", "otro","tres") 'hola.dos.otro.tres' unir_con_puntos("192", "168", "2") '192.168.2'
Por nombre, los argumentos son un diccionario, veamos un ejemplo:
def conexion(**kwargs): for kwarg in kwargs: print(kwarg, "=>", kwargs[kwarg]) conexion(ip="192.168.1.1", user="admin", pasword="1234") ip => 192.168.1.1 user => admin pasword => 1234
Y podemos mezclar ambas, por posición y por nombre, los argumentos que van sin nombre (nombre=valor) serán por posición (lista) y los que van nombre serán diccionario, veamos un ejemplo:
SyntaxError
.
def conexion(*ips, **datos): for ip in ips: print(f"conectando a la ip {ip} con los datos:") for dato in datos: print(dato, "=>", datos[dato]) conexion("192.168.1.1","10.0.0.1","172.16.1.2",usuario="admin",password="1234") conectando a la ip 192.168.1.1 con los datos: usuario => admin password => 1234 conectando a la ip 10.0.0.1 con los datos: usuario => admin password => 1234 conectando a la ip 172.16.1.2 con los datos: usuario => admin password => 1234
Podemos devolver uno o más valores con el comando return
def suma(a, b): return(a+b) suma(5,6) 11
También podemos devolver más de un valor, lo cual serán devuelto como una lista.
def suma(a, b): return(a, b, a+b) suma(4,6) (4, 6, 10)
Por defecto Python primero busca la variable dentro de la función (variables locales) y si no la encuentra busca fuera, variables globales (deben definirse antes de llamar a la función), pero SOLO para leer su contenido, no para modificarlo. Si intentamos modificar una variable global dentro de una función se produce una excepción UnboundLocalError
.
cadena1 = "global" def funcion(cadena2): print("cadena1 = ", cadena1) print("cadena2 = ", cadena2) funcion('parametro') cadena1 = global cadena2 = parametro
Para modificar una variable global desde dentro de una función debemos indicar en la función que esa variable es global
:
cadena1 = "global"
def funcion(cadena2):
global cadena1 <-- PERMITIR MODIFICAR
cadena2 += '.funcion'
cadena1 += '.funcion'
print("cadena1 = ", cadena1)
print("cadena2 = ", cadena2)
funcion('parametro')
cadena1 = global.funcion
cadena2 = parametro.funcion
Es importante tener en cuenta que si definimos una variable dentro de una función que se llame igual que una variable global, serán dos variables completamente diferentes usando dentro la función la que se definió en la misma.
cadena1 = "global"
def funcion(cadena2):
cadena2 += '.funcion'
cadena1 = 'funcion' <-- valor LOCAL
print("cadena1 = ", cadena1)
print("cadena2 = ", cadena2)
funcion('parametro')
cadena1 = funcion
cadena2 = parametro.funcion
print("cadena1 = ", cadena1)
cadena1 = global
Las funciones recursivas son funciones que se llaman a si mismas, por lo que es muy importante que haya un mecanismo que rompa el bucle infinito.
Pueden ser sin que retornen valores, simplemente que hagan algo, un ejemplo sencillo es una cuenta atrás:
def cuenta_atras(num): num -= 1 if num > 0: print(num) cuenta_atras(num) else: print("Boooooooom!") <-- SOLO al final de la última llamada print("Fin de la función", num) <-- se ejecuta tras cada llamada cuenta_atras(5) 4 3 2 1 ¡¡ BOOOM !! Valor de num al finalizar la función: 0 Valor de num al finalizar la función: 1 Valor de num al finalizar la función: 2 Valor de num al finalizar la función: 3 Valor de num al finalizar la función: 4
Como podemos ver hay que tener cuidado con lo que se pone al final de la función, una vez se vuelve a llamar a la misma función, porque una vez se valla terminando el bucle las funciones irán terminando desde fueron llamadas.
También pueden devolver un valor que se puede usar para hacer alguna tarea repetitiva, un ejemplo típico es calcular el factoria:
def factorial(num): print("Valor inicial ->",num) if num > 1: num = num * factorial(num -1) print("valor final ->",num) return num print(factorial(5)) Valor inicial - 5 <-- 5 x llamada Valor inicial - 4 <-- 4 x llamada Valor inicial - 3 <-- 3 x llamada Valor inicial - 2 <-- 2 x llamada Valor inicial - 1 <-- 1 (termina la recursividad) valor final - 1 <-- 1 (valor devuelto) valor final - 2 <-- 2 x 1 = 2 (valor devuelto) valor final - 6 <-- 3 x 2 = 6 (valor devuelto) valor final - 24 <-- 4 x 6 = 24 (valor devuelto) valor final - 120 <-- 5 x 24 = 120 (valor devuelto) 120
Más información en: funciones