Funciones: define y reutiliza
Una función es un bloque de código con nombre que recibe valores (argumentos),
hace algo y devuelve un resultado. Se define con def y se invoca con paréntesis. Es la pieza
básica para no repetir código.
def area_circulo(radio):
# return devuelve el resultado al que llama
return 3.1416 * radio ** 2
a = area_circulo(5)
print(f"{a:.2f} u²") # 78.54 u²
1.1 · Parámetros y valores por defecto
Un parámetro con = valor es opcional. Los opcionales siempre van después de
los obligatorios:
def saludar(nombre, idioma="es"):
if idioma == "es":
return f"Hola, {nombre}"
return f"Hello, {nombre}"
print(saludar("Ana")) # Hola, Ana (usa el default)
print(saludar("Ana", "en")) # Hello, Ana (lo pasamos explícito)
return, devuelve None. Muchas
funciones «hacen cosas» (como print()) y no devuelven nada útil: no intentes guardar su
resultado.1.2 · Argumentos por nombre (keywords)
Puedes llamar a una función pasando los valores en orden (posicionales) o indicando el nombre del parámetro. Por nombre es más legible y no dependes del orden:
def crear_usuario(nombre, email, rol="alumno"):
return {"nombre": nombre, "email": email, "rol": rol}
u1 = crear_usuario("Luis", "l@x.es")
u2 = crear_usuario(email="r@x.es", nombre="Rita", rol="admin")
Argumentos avanzados y lambda
2.1 · *args: un número variable de posicionales
*args recoge TODOS los argumentos posicionales en una tupla. Ideal para sumar n números:
def suma_total(*args):
return sum(args)
print(suma_total(1, 2)) # 3
print(suma_total(1, 2, 3, 4)) # 10
2.2 · **kwargs: argumentos nombrados variables
**kwargs recoge los argumentos por nombre en un diccionario:
def configurar(**kwargs):
for clave, valor in kwargs.items():
print(f"{clave} = {valor}")
configurar(host="localhost", puerto=5432, modo="debug")
2.3 · Lambda: funciones pequeñas y anónimas
Una lambda es una función de una sola expresión, sin def ni return.
Se usa sobre todo como «clave» de sorted, min, max o con map/filter:
personas = [("Ana", 25), ("Luis", 40), ("Rita", 30)]
# ordenar por la edad (segundo elemento de cada tupla)
ordenadas = sorted(personas, key=lambda persona: persona[1])
# la persona más joven
mas_joven = min(personas, key=lambda p: p[1])
print(ordenadas) # [('Ana', 25), ('Rita', 30), ('Luis', 40)]
print(mas_joven) # ('Ana', 25)
lambda x: x[1] significa «fungión que recibe x y devuelve
x[1]». La sintaxis es lambda parámetros: expresión.2.4 · Funciones como ciudadanas de primera clase
En Python las funciones son valores: puedes guardarlas en variables, pasarlas como argumento y devolverlas. Eso abre la puerta a la programación funcional:
def aplicar(funcion, valor):
return funcion(valor)
def doble(x):
return x * 2
print(aplicar(doble, 5)) # 10
print(aplicar(lambda x: x * x, 3)) # 9
Ámbitos, docstrings y buenas prácticas
3.1 · Ámbito local y global
Las variables creadas dentro de una función son locales (viven solo ahí). Las creadas al
nivel superior del programa son globales. Para leer una global desde una función no hace
falta nada; para modificarla, necesitas global:
contador = 0 # global
def incrementar():
global contador # indica que vamos a modificar la global
contador += 1
incrementar()
incrementar()
print(contador) # 2
global es mala práctica. Lo ideal es que las funciones reciban
valores por parámetros y devuelvan resultados. ¿Quieres una lista global modificada? Pásala como argumento o
devuélvela. Las globals hacen el código difícil de seguir y de probar.3.2 · Docstrings: la documentación en el código
Un docstring es un texto con comillas triples justo después de def. Explica
QUÉ hace la función, qué parámetros espera y qué devuelve. Herramientas como help() lo leen:
def precio_con_iva(precio, iva=15):
"""Devuelve el precio con el IVA aplicado y redondeado a 2 decimales.
Args:
precio (float): precio base
iva (int): porcentaje de IVA (por defecto 15)
Returns:
float: precio final con IVA
"""
return round(precio * (1 + iva / 100), 2)
print(precio_con_iva(100)) # 115.0
print(precio_con_iva(100, 21)) # 121.0
3.3 · Buenas prácticas
calcular_total(), guardar(), nunca cosas().return el resultado en lugar de solo printar (más fácil de probar).from modulo import *: importa lo que necesites.Módulos y excepciones
4.1 · Módulos: importar código
Un módulo es un archivo .py con funciones, clases y variables que puedes
reutilizar en otros programas mediante import:
# módulos de la biblioteca estándar
import math
import random
import datetime as dt # alias
from collections import Counter # importar algo concreto
print(math.sqrt(16)) # 4.0
print(random.randint(1, 6)) # un número del 1 al 6
print(dt.date.today()) # 2026-09-01
También puedes importar tus propios archivos. Si creas
utilidades.py con una función, otro archivo hace from utilidades import generar_id.
Eso es lo que separa un script de un programa modular.
4.2 · pip: instalar librerías de terceros
pip instala paquetes del índice oficial (pypi.org):
$ pip install requests # instala un paquete
$ pip list # los paquetes instalados
$ pip uninstall requests # desinstala
$ pip freeze # lista versiones (para guardar en requirements.txt)
4.3 · Excepciones: errores controlados
En Python, cuando algo falla se lanza una excepción. Si no la capturas, el programa
muere. Con try/except la controlas tú:
texto = input("Edad: ")
try:
edad = int(texto)
print(f"Dentro de 10 años tendrás {edad + 10}")
except ValueError:
print("Eso no es un número válido")
Excepciones habituales: ValueError (valor inválido para int()),
KeyError (dict sin clave), IndexError (índice fuera de rango), FileNotFoundError
(fichero inexistente), ZeroDivisionError. Puedes encadenar except con distintos
tipos.
Exception a secas para «tapar» errores. Captura el tipo
concreto, o usa else (si va bien) y finally (siempre se ejecuta, p. ej. para cerrar
un archivo).Ficheros: persistir datos
Para que un programa recuerde datos entre ejecuciones hay que guardarlos en disco. Python trabaja con ficheros de texto y con formatos estructurados como CSV y JSON.
5.1 · Leer y escribir con with
El bloque with cierra el archivo automáticamente al terminar, incluso si hay
un error. Es la forma recomendada, nunca olvides cerrarlo:
# escribir (w: sobrescribe, a: añade al final)
with open("notas.txt", "w", encoding="utf-8") as f:
f.write("Línea uno\n")
f.write("Línea dos\n")
# leer todo el contenido
with open("notas.txt", encoding="utf-8") as f:
contenido = f.read()
print(contenido)
encoding="utf-8" es imprescindible para los acentos y la ñ en español.
Sin él, Windows podría dar errores de codificación al leer tu archivo.5.2 · Leer por líneas
with open("notas.txt", encoding="utf-8") as f:
for linea in f:
print(linea.strip()) # strip quita el salto de línea
5.3 · Trabajar con JSON
JSON es el formato universal para intercambiar datos. Python lo maneja con el módulo json:
import json
datos = {"nombre": "Ana", "edad": 25, "activo": True}
# guardar (serializar)
with open("usuario.json", "w", encoding="utf-8") as f:
json.dump(datos, f, ensure_ascii=False, indent=2)
# leer (deserializar)
with open("usuario.json", encoding="utf-8") as f:
recuperado = json.load(f)
print(recuperado["nombre"]) # Ana — y ya es un dict de Python
csv. Aunque lo
verás en el bloque 5 a fondo, ya puedes hacer la idea: csv.reader y csv.DictWriter
mueven tablas con una API parecida a la anterior.✍️ Los 15 ejercicios del bloque
Crea la carpeta python-b3/ y un archivo .py por ejercicio. Practica ejecutando
cada programa; cuando un ejercicio pida funciones, define la función y luego pruébala con varios
casos con print().
Sección A · Funciones básicas
1 Saludar con default
Define saludar(nombre, mensaje="Hola") que devuelva f"{mensaje}, {nombre}".
Llámala dos veces: con el default y con mensaje personalizado.
Ver solución
def saludar(nombre, mensaje="Hola"):
return f"{mensaje}, {nombre}"
print(saludar("Ana"))
print(saludar("Ana", "Buenos días"))
2 Triple función
Crea cuadrado(x), cubo(x) y factorial(n) (con un bucle).
Prueba: cuadrado(3)=9, cubo(2)=8, factorial(5)=120.
Ver solución
def cuadrado(x):
return x ** 2
def cubo(x):
return x ** 3
def factorial(n):
resultado = 1
for i in range(2, n + 1):
resultado *= i
return resultado
print(cuadrado(3), cubo(2), factorial(5))
3 Palabra invertida
Define invertir(texto) que devuelva el texto invertido con slicing y prueba con
"Python" → "nohtyP".
Ver solución
def invertir(texto):
return texto[::-1]
print(invertir("Python")) # nohtyP
4 Suma con *args
Define suma_varios(*args) que devuelva la suma de todos los argumentos usando
sum(). Prueba con 2, 3 y 5 valores distintos.
Ver solución
def suma_varios(*args):
return sum(args)
print(suma_varios(1, 2)) # 3
print(suma_varios(1, 2, 3)) # 6
print(suma_varios(1, 2, 3, 4, 5)) # 15
5 Media de notas con *args
Define media(*notas) que devuelva la media de las notas. Si no recibe ninguna, devuelve
0. Prueba con varias combinaciones.
Ver solución
def media(*notas):
if not notas:
return 0
return sum(notas) / len(notas)
print(media(7, 8, 9)) # 8.0
print(media()) # 0
6 Imprimir configuración con **kwargs
Define mostrar_config(**kwargs) que recorra kwargs.items() y muestre
clave = valor por línea. Prueba pasando 3 configuraciones.
Ver solución
def mostrar_config(**kwargs):
for clave, valor in kwargs.items():
print(f"{clave} = {valor}")
mostrar_config(host="localhost", puerto=8080, debug=True)
Sección B · Lambda
7 Ordenar personas por edad
Dada personas = [("Ana", 25), ("Luis", 40), ("Rita", 30)], ordénalas por edad con
sorted + lambda. Muestra el resultado.
Ver solución
personas = [("Ana", 25), ("Luis", 40), ("Rita", 30)]
ordenadas = sorted(personas, key=lambda p: p[1])
print(ordenadas) # [('Ana', 25), ('Rita', 30), ('Luis', 40)]
8 Más barato y más caro
Dada precios = {"ratón": 12, "teclado": 45, "pad": 8}, usa min y max
con key=precios.get para mostrar el producto más barato y el más caro.
Ver solución
precios = {"ratón": 12, "teclado": 45, "pad": 8}
print(min(precios, key=precios.get)) # pad
print(max(precios, key=precios.get)) # teclado
Sección C · Excepciones y ficheros
9 Dividir con seguridad
Define dividir(a, b) que devuelva a / b pero capture en
ZeroDivisionError devolviendo None. Prueba con dividir(10, 2) y
dividir(10, 0).
Ver solución
def dividir(a, b):
try:
return a / b
except ZeroDivisionError:
return None
print(dividir(10, 2)) # 5.0
print(dividir(10, 0)) # None
10 Edad robusta
Usa try/except ValueError para convertir a entero la entrada de input().
Si falla, muestra «edad no válida»; si funciona, muestra f"Edad correcta: {edad}".
Ver solución
texto = input("Tu edad: ")
try:
edad = int(texto)
print(f"Edad correcta: {edad}")
except ValueError:
print("edad no válida")
11 Escribir y leer
Crea un archivo mensaje.txt con open(..., "w"), escribe 3 líneas, y luego
lee el contenido completo con with open(..., encoding="utf-8") y muéstralo.
Ver solución
with open("mensaje.txt", "w", encoding="utf-8") as f:
f.write("primera línea\n")
f.write("segunda línea\n")
f.write("tercera línea\n")
with open("mensaje.txt", encoding="utf-8") as f:
print(f.read())
12 Número de líneas
Dado mensaje.txt del ejercicio anterior, cuenta y muestra cuántas líneas tiene usando
for linea in f. Debe salir 3.
Ver solución
cuenta = 0
with open("mensaje.txt", encoding="utf-8") as f:
for linea in f:
cuenta += 1
print(f"El fichero tiene {cuenta} líneas")
13 Guardar y leer JSON
Con json.dump guarda {"nombre": "Ana", "edad": 25} en
usuario.json con indent. Después cárgalo con json.load y muestra
nombre y edad.
Ver solución
import json
datos = {"nombre": "Ana", "edad": 25}
with open("usuario.json", "w", encoding="utf-8") as f:
json.dump(datos, f, ensure_ascii=False, indent=2)
with open("usuario.json", encoding="utf-8") as f:
carga = json.load(f)
print(carga["nombre"], carga["edad"])
14 Módulo propio
Crea utilidades.py con la función generar_id() que devuelva un número entero
aleatorio del 1 al 1000 (usa random.randint). Crea otro archivo main.py que
importe la función y genere 3 ids imprimiéndolos.
Ver solución
# utilidades.py
import random
def generar_id():
return random.randint(1, 1000)
# main.py
from utilidades import generar_id
print(generar_id())
print(generar_id())
print(generar_id())
15 🏆 Proyecto integrador: «gestor de gastos»
Crea gastos.py: un programa que registra gastos y los persiste en un fichero
JSON. Requisitos:
- Carga los gastos existentes de
gastos.json(conjson.load; si no existe, empieza con una lista vacía dentro de untry/except FileNotFoundError). - Menú
while True: 1 añadir gasto (concepto y importe confloat), 2 ver la lista, 3 total acumulado, salir. - Usa una función
guardar(gastos)que escriba conjson.dump. - Al añadir, valida el importe con
try/except ValueError. - Comentarios y nombres en snake_case.
Ver una solución posible
# Gestor de gastos persistente
import json
def guardar(gastos):
with open("gastos.json", "w", encoding="utf-8") as f:
json.dump(gastos, f, ensure_ascii=False, indent=2)
try:
with open("gastos.json", encoding="utf-8") as f:
gastos = json.load(f)
except FileNotFoundError:
gastos = []
while True:
print("\n1 añadir · 2 ver · 3 total · salir")
opcion = input("Elige: ").strip().lower()
if opcion == "1":
concepto = input("Concepto: ")
try:
importe = float(input("Importe: "))
except ValueError:
print("Importe no válido")
continue
gastos.append({"concepto": concepto, "importe": importe})
guardar(gastos)
print(f"Añadido: {concepto} ({importe:.2f} €)")
elif opcion == "2":
for g in gastos:
print(f"- {g['concepto']}: {g['importe']:.2f} €")
elif opcion == "3":
total = sum(g["importe"] for g in gastos)
print(f"Total: {total:.2f} €")
elif opcion == "salir":
break
📝 Examen del Bloque 3
Dos partes: un test teórico autoevaluable (mínimo 70 %) y un reto práctico de autocorrección con laboratorio.
Parte B · Reto «promedio calculadora»
Crea calculadora.py: funciones puras de cálculo
(sumar, restar, multiplicar, dividir) más
suma_varios(*args), una lambda de ordenación y un try/except.
Valida tú mismo/a estos criterios. Mínimo para aprobar: 6 de 8 casillas.
Cumplidos: 0 / 8