- ¿Qué es la determinación en una función?
- ¿Qué son los efectos secundarios?
- Funciones puras (Pure Functions)
- Funciones impuras
- Closures: funciones puras e impuras
- Resumen
Uno de los problemas más comunes en programación es el de las dependencias implícitas y los efectos secundarios ⚠️ Estos pueden causar errores inesperados y hacer que el código sea difícil de entender y depurar.
Para evitar estos problemas, se recomienda usar funciones puras siempre que sea posible. En este curso, aprenderás las propiedades clave de las funciones y cómo dividir mejor el código en funciones útiles.
¿Qué es la determinación en una función?
Una función determinada siempre devuelve el mismo resultado para los mismos valores de entrada sin depender del estado del programa ni de factores externos. Su resultado es predecible y consistente ✅.
⏬ Ejemplo de una función NO determinada:
import random
def get_random_number():
return random.randint(1, 10)
Cada vez que llamamos get_random_number(), obtenemos un número diferente:
get_random_number() # 3
get_random_number() # 7
get_random_number() # 1
Ahora, veamos una función determinada:
def multiply(a, b):
return a * b
Siempre devuelve el mismo resultado para los mismos argumentos:
multiply(2, 3) # 6
multiply(2, 3) # 6
multiply(2, 3) # 6
Esto hace que multiply() sea una función determinada, ya que su resultado solo depende de sus parámetros y no cambia en cada ejecución.
⚠️ No todas las funciones deben ser determinadas. Algunas, como generar números aleatorios o obtener la fecha actual, necesitan ser no determinadas. Sin embargo, es buena práctica dividir el código para que la mayor parte de él sea determinista y predecible.
¿Qué son los efectos secundarios?
Un efecto secundario (side effect) ocurre cuando una función modifica algo fuera de su propio ámbito, como:
- Escribir en un archivo
- Leer datos de una base de datos
- Enviar o recibir datos de la red
- Modificar variables globales
- Mostrar datos en consola
⏬ Ejemplo de una función con efecto secundario:
def print_hello():
print("Hello, world!") # ❌ Modifica el estado de la consola
Esta función imprime un mensaje en la consola, lo que significa que tiene un efecto secundario. Si llamamos a la función print_hello, altera el estado del programa al imprimir un mensaje en la consola. Pero no devuelve ningún valor:
print_hello()
# Hello, world!
⏬ Otro ejemplo de efecto secundario es modificar variables fuera de la función:
def modify_dict(dictionary):
dictionary["key"] = 42 # ❌ Modifica el diccionario original
¿Por qué evitar efectos secundarios?
❌ Dificultan la comprensión del código.
❌ Hacen que las funciones dependan del contexto.
❌ Complican las pruebas y depuración.
⚠️ Pero no podemos evitarlos por completo. Todas las aplicaciones necesitan interactuar con el mundo exterior (leer archivos, enviar datos, etc.). Lo importante es minimizar su impacto y controlarlos bien.
💡 Ejemplo ideal: Una aplicación que convierte archivos de texto a PDF solo debería tener dos efectos secundarios:
- Leer el archivo de entrada.
- Guardar el archivo PDF.
Todo lo demás debe ser código limpio y sin efectos secundarios.
Funciones puras (Pure Functions)
Una función pura es aquella que:
- Siempre devuelve el mismo resultado para los mismos argumentos.
- No tiene efectos secundarios.
- No usa ni modifica variables globales.
- No cambia los valores de los parámetros que recibe.
⏬ Ejemplo de funciones puras:
def add_numbers(x, y):
return x + y # ✅ No cambia nada fuera de la función
# Crear una nueva lista en lugar de modificar la original
def add_user(users, new_user):
return [*users, new_user] # ✅ No modifica la lista original
# Parseo de HTML sin modificar nada en el código
import html5lib
def parse_html(html):
return html5lib.parse(html)
# ✅ Solo devuelve un resultado basado en el argumento
Ventajas de las funciones puras:
✅ Fáciles de probar: Solo se necesita verificar la salida.
✅ Reutilizables y predecibles.
✅ Compatibles con programación asincrónica y multiprocesos.
Funciones impuras
Las funciones impuras (Dirty Functions) sí afectan el estado del programa y pueden interactuar con recursos externos, como bases de datos o archivos.
⏬ Ejemplo:
def save_to_file(data):
with open("archivo.txt", "w") as f:
f.write(data) # ❌ Modifica un archivo externo
Closures: funciones puras e impuras
Un closure (cierre) es una función que recuerda el entorno en el que fue definida. Puede ser puro o impuro, dependiendo de si modifica variables de su entorno.
⏬ Ejemplo de closure puro (solo lee valores externos, pero no los cambia):
def outer(x):
def inner(y):
return x + y # ✅ No modifica 'x', solo la usa
return inner
add_5 = outer(5)
print(add_5(10)) # ✅ 15
⏬ Ejemplo de closure impuro (modifica variables externas):
def outer():
count = 0
def inner():
nonlocal count # ❌ Modifica 'count'
count += 1
return count
return inner
counter = outer()
print(counter()) # ❌ 1
print(counter()) # ❌ 2
⏬ Otro ejemplo impuro con objetos mutables:
def outer(lst):
def inner(item):
lst.append(item) # ❌ Modifica la lista original
return lst
return inner
add_to_list = outer([])
print(add_to_list(1)) # ❌ [1]
print(add_to_list(2)) # ❌ [1, 2]
Resumen
✅ Las funciones puras son más fáciles de entender y probar.
❌ Las funciones impuras pueden ser necesarias, pero deben usarse con cuidado.
Para acceder completo a curso necesitas un plan básico
El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.