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:

Importante
Los argumentos de posición DEBEN ir antes que los argumentos de nombre, si colocamos un valor sin nombre tras un argumento con nombre dará error 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

Retro

Lugares

Redes

Sistemas

Varios