¡Esta es una revisión vieja del documento!
Tabla de Contenidos
Funciones
Bloque perteneciente al curso Introducción a la programación con Python.
Una función es un elemento de un programa que agrupa sentencias de programación que luego pueden ser utilizadas desde cualquier otra parte del programa. Las funciones pueden, opcionalmente, recibir datos de entrada (llamados parámetros o argumentos) y retornar datos de salida o resutlados.
Definición de nuevas funciones
La definición de una función debe hacerse una única vez antes de ser utilizada.
Una vez definida, la función perdura hasta el final de la ejecución del código del programa.
def siguiente_numero(n): return n + 1
Declarar o definir una función no la ejecuta.
type(siguiente_numero) # function
Llamada a función
Llamar a una función es hacer uso de la misma (ejecutar su código), pasándole entre paréntesis los datos de entrada (si los necesita) y recogiendo el resultado. Dicho resultado puede almacenarse en una variable.
siguiente_nunmero(8) # 9 x = 2 y = siguiente_numero(x) x, y # (2, 3)
Definición alternativa de función
Explicitando tipos de datos en parámetros y valor devuelto:
def saludar(nombre: str) -> str: ''' Esta función genera un saludo personalizado a la persona cuyo nombre se da como parámetro ''' return "Hola " + nombre
nombre = input("¿Cómo te llamas?") resultado = saludar(nombre) print(resultado)
from math import sqrt def segundo_grado(a: float, b: float, c: float) -> tuple: ''' Busca las raíces reales de una ecuación de segundo grado con la forma a*x**2 + b*x + c = 0 ''' d = b**2 - 4 * a * c if d >= 0: x1 = (-b + sqrt(d)) / (2 * a) x2 = (-b - sqrt(d)) / (2 * a) return x1, x2 else: return None
help(segundo_grado) Help on function
Uso:
a = float(input("Coeficiente a: ")) b = float(input("Coeficiente b: ")) c = float(input("Coeficiente c: ")) resultado = segundo_grado(a, b, c) if resultado: r, s = resultado print(r, s) else: print("No tiene soluciones reales")
Versión con excepciones (en lugar de devolver 'None'):
from math import sqrt def segundo_grado(a: float, b: float, c: float) -> tuple: ''' Busca las raíces reales de una ecuación de segundo grado con la forma a*x**2 + b*x + c = 0 En caso de no tener soluciones reales, causa excepción ''' d = b**2 - 4 * a * c if d >= 0: x1 = (-b + sqrt(d)) / (2 * a) x2 = (-b - sqrt(d)) / (2 * a) return x1, x2 else: raise ValueError(f"La ecuación {a}*x**2 + {b}*x + {c} = 0 no tiene soluciones reales")
Uso:
Coeficiente a: 1 Coeficiente b: 0 Coeficiente c: 1 AQUI ERROR
Podemos capturar el error de la siguiente manera con try y except:
a = float(input("Coeficiente a: ")) b = float(input("Coeficiente b: ")) c = float(input("Coeficiente c: ")) try: resultado = segundo_grado(a, b, c) r, s = resultado print(r, s) except ValueError as error: print(error)
Cuando lo usamos:
Coeficiente a: 1 Coeficiente b: 0 Coeficiente c: 1 La ecuación 1.0*x**2 + 0.0*x + 1.0 = 0 no tiene soluciones reales.
Paso de parámetros
Parámetros posicionales
Van en el mismo orden que están definidos:
segundo_grado(1, 2, -3)
Parámetros con nombre
Se pueden indicar en cualquier orden especificando el nombre de cada uno:
segundo_grado(b = 2, a = 1, c = -3)
Parámetros con valores por defecto
Se pueden indicar valores por defecto para los parámetros, que serán usados si el valor para el parámetro en cuestión se omite.
def calcular_impuesto(base, iva=21) return base * iva / 100.0
Uso:
# Omitimos parámetro iva, así que toma un valor por defecto 21 calcular_impuesto(1000) # 210.0 # Indicamos explícitamente el valor del parámetro calcular_impuesto(1000, 4) # 40.0
Funciones con un número variable de parámetros
Las funciones pueden recibir parámetros variables:
- Si son posicionales (sin nombre), se recogen en una lista
- Si son con nombre, se recogen en un diccionario.
Con el asterisco (*) indicamos que se esperan una cantidad indeterminada de parámetros:
def calcular_promedio(*datos: float) -> float: ''' Calcula el promedio de varios datos de tipo numérico. El número de datos puede ser variable ''' # Primero comprobamos que hay datos. Si no hay, que devuelva 'None' if len(datos) == 0: return None # A continuación, declaramos una variable local que comienza con el valor 0 y en la que # vamos a ir sumando todo suma = 0.0 for d in datos: suma += d # Después de recorrer los datos uno a uno, lo dividimos entre el número de datos y ahí # tenemos el promedio return suma / len(datos)
Uso:
calcular_promedio(1, 2, 6, 5, 4, 0, 8, -3) # 2.875 calcular_promedio(5, 10) # 7.5
Con dos asteriscos (**):
def calcular_nota_final(**notas): nota = 0.0 if 'examen' in notas: nota = notas['examen'] if 'recuperacion' in notas and notas['recuperacion'] > nota: nota = notas['recuperacion'] return nota
Uso:
calcular_nota_final(examen=7.5) # 7.5 calcular_nota_final(examen=4, recuperacion=6.5) # 6.5
def vale_todo(*args, **kwargs): for d in args: print("Argumento posicional: ", d) for k, v in kwargs.items(): print(f"Argumento con nombre: {k}={v}")
Uso:
vale_todo("alpha", "bravo", c = "charlie", d = "delta")
# Argumento posicional: alpha
# Argumento posicional: bravo
# Argumento con nombre: c=charlie
# Argumento con nombre: d=delta
Otro ejemplo:
l = ["alpha", "bravo", "charlie"] d = {"d": 2, "e": -5} # Para desagregar la lista y el diccionario anteriores y pasárselos como # argumentos variables a la función: vale_todo(*l, **d)
Funciones anónimas en línea (lambda)
En Python, una función Lambda se refiere a una pequeña función anónima. Las llamamos “funciones anónimas” porque técnicamente carecen de nombre.
cubo = lambda x: x*x*x cubo(5) # 125 type(cubo) # function
palabras = ["charlie", "delta", "foxtrot", "echo"] # Ordenación según el orden por defecto para las strings: orden lexicográfico sorted(palabras) # ['charlie', 'delta', 'echo', 'foxtrot'] # Se le puede pasar una función a 'sorted' sorted(palabras, key=len) # ['echo', 'delta', 'charlie', 'foxtrot'] def logitud_inv(s: str) -> float: return -len(s) sorted(palabras, key=longitud_inv) # ['charlie', 'foxtrot', 'delta', 'echo'] # Utilizando una función anónima: sorted(palabras, key=lambda s: -len(s)) # ['charlie', 'foxtrot', 'delta', 'echo']
# Crear una lista con las palabras que contenga 'e': list(filter(lambda s: 'e' in s, palabras)) # ['charlie', 'delta', 'echo']
# Crear una lista de elementos "mapeados" con la función que convierte a mayúsculas list(map(lambda p: p.upper(), palabras)) # ["CHARLIE", "DELTA", "FOXTROT", "ECHO"]
Funciones que no retornan valor
def contar_hasta(n : int): for i in range(n): print(i+1)
Resultado de ejecución:
contar_hasta(10) 1 2 3 4 5 6 7 8 9 10
Existe una instrucción en Python que no hace nada: pass. Puede ser útil si no queremos definir una función o algo por el momento:
def do_nothing(): pass
Funciones generadoras
Funciones que no devuelven un resultado, sino un objeto que puede utilizarse como iterable y que en cada iteración provoca que la función genere un nuevo valor.
def impares(tope: int = None): i = 1 while i < tope: yield i # parecido a 'return', pero no finaliza la función i += 2 impares(20) # generator object g = impares(20) next(g) # 1 next(g) # 3 next(g) # 5 next(g) # 7 for a in impares(20): print(a) # 1 # 3 # 5 # 7 # 9 # 11 # 13 # 15 # 17 # 19
from random import randint def combinacion_loteria(): combinacion_ganadora = set() while len(combinacion_ganadora) < 6: n = randint(1, 49) if n in combinacion_ganadora: continue yield n combinacion_ganadora.add(n) # La usamos con un 'for' for b in combinacion_loteria(): print(b) # 45 # 36 # 43 # 1 # 31 # 11
Secuencia de Fibonacci. Cada término de la sucesión se define como la suma de los dos anteriores:
0 1 # 1 + 0 1 # 1 + 0 2 # 1 + 1 3 # 2 + 1 5 # 3 + 2 8 # 5 + 3 13 # 8 + 5 21 # 13 + 8
def fibonacci(tope : int): a = 0 b = 1 while not tope or a < tope: yield a c = a + b b, a = c, b # a = b; b = c
for t in fibonacci(200): print(t)
Vamos a buscar la proporción áurea (ojo, que esto no estoy seguro de que esté bien):
phi = 1 phi0 = 0 g = fibonacci() next(g) a = next(g) b = next(g) while abs(phi - phi0) > 1e-9: b = a a = next(g) phi0 = phi phi = b / a print(f"phi = {phi:.12f}")
Funciones recursivas
Funciones que se llaman a sí mismas. La recursividad es útil como método de simplificación de un problema complejo dividiendo el problema en subproblemas del mismo tipo.
Un buen ejemplo es el factorial de un número entero:
def factorial(n : int) -> int: if n > 0: return n * factorial(n - 1) else: return 1
Uso:
factorial(5) # 120 factorial(0) # 1
Ejemplos prácticos
Torres de Hanoi
Ejemplo clásico de recursividad.
El juego consiste en pasar todos los discos desde el poste ocupado (es decir, el que posee la torre) a uno de los otros postes vacíos. Para realizar este objetivo, es necesario seguir tres simples reglas:
- Solo se puede mover un disco cada vez y para mover otro los demás tienen que estar en postes.
- Un disco de mayor tamaño no puede estar sobre uno más pequeño que él mismo.
- Solo se puede desplazar el disco que se encuentre arriba en cada poste.
La fórmula para encontrar el número de movimientos necesarios para transferir n discos desde un poste a otro es 2^n - 1
def hanoi(n, origen='A', destino='C', auxiliar='B'): if n > 1: hanoi(n - 1, origen, auxiliar, destino) print(f"Mover disco de {origen} a {destino}") if n > 1: hanoi(n - 1, auxiliar, destino, origen)
