6 módulos útiles en Python
Python tiene una variedad de módulos para una variedad de tareas, sin embargo, podríamos decir (al menos en mi opinión) que existen algunos que se usan muy a menudo, casi siempre, por eso, he decidido hacer este articulo resaltando los 6 módulos que ya vienen con Python y que son bastante útiles y todo el mundo debería de conocer.
El primero de todos es el módulo os:
Módulo os
El módulo os, es bastante útil, y la verdad, cuando empezamos aprender Python, es uno de los principales que se enseña, ya que nos permite trabajar con el sistema operativo, navegar por directorios, crear carpetas, listar archivos, etc.
Por ejemplo:
import os # Obtener el directorio actual print("Directorio actual:", os.getcwd()) # Cambiar de directorio os.chdir("..") # Ir al directorio padre print("Directorio después de cambiar:", os.getcwd())
Otro ejemplo:
# Conocer el sistema operativo print("Sistema operativo:", os.name) # 'posix' en Unix/Linux, 'nt' en Windows
Y ahora un ejemplo algo más típico, listar todos los archivos (incluyendo carpetas) de un determinado directorio:
# Listar archivos del directorio (carpetas y archivos) archivos = os.listdir(".") print("Archivos en el directorio actual:", archivos)
Hagamos un ejemplo más completo y… “profundo”.
Ahora mostraremos solo los archivos de un directorio. Las carpetas no nos interesan.
Si queremos ver los archivos de un directorio específico, necesitamos pasar la ruta del directorio deseado como argumento a os.listdir(). Pero ahora también necesitamos usar os.path.join() para construir las rutas completas para cada archivo o carpeta que haya en el directorio, ya que os.path.isfile() necesita la ruta completa para comprobar si un elemento es un archivo o no.
Ejemplo:
# Especificar la ruta del directorio directorio = r"C:\Users\nosfe\Python" # Listar solo archivos del directorio especificado archivos = [f for f in os.listdir(directorio) if os.path.isfile(os.path.join(directorio, f))] print("Archivos en el directorio:", archivos)
Mencionar que, al especificar la ruta del directorio, al menos en Windows, debemos usar una cadena con prefijo r (raw string) para evitar que los caracteres de escape (como \n) causen problemas.
Un raw string, lo que hace es cambiar el significado de los caracteres de escape que comienzan con una barra invertida (\). En otras palabras, se entiende al carácter ‘\’ como cualquier otro. Por ejemplo, si en una cadena se encuentra ‘\n’ (en el ejemplo \nosfe), no lo tiene en cuenta como un salto de línea.
Sigamos…
¿Y si ahora queremos listar los archivos, pero solo los que tengan una determinada extensión?
Dejaremos el mismo código que antes, pero añadiremos otra comprobación más usando el método str.endwith(), el cual devolverá true si el archivo pasado termina con la extensión especificada o false en caso contrario.
Por ejemplo, me interesa mostrar los archivos de Excel (que terminan en .xlsx) que tengo en el directorio documentos:
directorio = r"C:\Users\nosfe\Documents" archivos_xlsx = [f for f in os.listdir(directorio) if os.path.isfile(os.path.join(directorio, f)) and f.endswith(".xlsx")] print("Archivos .xlsx en el directorio:", archivos_xlsx)
¿Pero y si queremos listar también los archivos que haya en los subdirectorios?
Simplemente usamos os.walk() para explorar subdirectorios:
for raiz, subdirectorios, archivos in os.walk(directorio): print(f"Explorando: {raiz}") for archivo in archivos: print(f" - {archivo}")
Crear y eliminar archivos:
Para eliminar un archivo usamos os.remove():
os.remove("nombre_archivo.extension")
Aunque siempre es recomendable comprobar antes si el archivo en cuestión existe o no con os.path.exists:
archivo = r"C:\laragon\www\PHP\temas blog\ejemplos\5modulos\borrar.txt" if os.path.exists(archivo): os.remove(archivo) print("Archivo eliminado.") else: print("El archivo no existe.")
Si por el contrario queremos crear un archivo, debemos usar la función open() en modo ‘w’ o ‘x’:
# Crear archivo: # 'w': Crea el archivo si no existe. Si existe, lo sobrescribe. # 'x': Crea el archivo, pero genera un error (FileExistsError) si el archivo ya existe. with open("archivo.txt", "w") as archivo: archivo.write("Contenido para el archivo creado.") print("Archivo creado con éxito.")
Para crear una carpeta sencillamente usamos:
os.mkdir("nombre_carpeta")
Una vez más, es útil comprobar antes si existe o no:
if not os.path.exists("carpetita"): os.mkdir("carpetita") print("Directorio creado con éxito.") else: print("El directorio ya existe.")
Para eliminar un directorio, tenemos dos opciones dependiendo de si este se encuentra vacío o no.
- Si está vacío usaremos os.rmdir().
- Si el directorio tiene algún contenido, usaremos shutil.rmtree().
os.rmdir():
if os.path.exists("carpetita"): os.rmdir("carpetita") # Solo funciona si el directorio está vacío print("Directorio eliminado con éxito") else: print("El directorio no existe.")
shutil.rmtree():
Aquí debemos de cambiar de modulo y usar el módulo shutil 😲:
import shutil # Eliminar un directorio con todo su contenido if os.path.exists("carpetita"): shutil.rmtree("carpetita") print("Directorio eliminado con éxito.") else: print("El directorio no existe.")
Modulo sys
El módulo sys nos permite interactuar con el entorno del interprete de Python, así como manejar la línea de comandos permitiendo leer los argumentos que le pasamos, etc.
Veamos algunos ejemplos.
El ejemplo más típico es usar sys.argv para pasar una lista de argumentos a un programa Python desde la línea de comandos.
El primer elemento es el nombre del script y los siguientes son los argumentos proporcionados.
Por ejemplo, en mi caso creo un archivo Python que se llama “modulo_sys.py” y contiene lo siguiente:
import sys # Lista de todos los argumentos pasados print("Argumentos pasados al programa:", sys.argv) # sys.argv[0] siempre será el nombre del script print("Nombre del script Python:", sys.argv[0]) # Otros argumentos if len(sys.argv) > 1: print("Otros argumentos:", sys.argv[1:]) else: print("No se pasaron argumentos adicionales.")
Si ahora abrimos la consola/línea de comandos y escribimos:
python modulo_sys.py Hola mundo
El resultado es:
Como ves es útil porque podemos crear un programa y usarlo desde la línea de comandos.
Veamos algunos ejemplos más.
Ahora vamos hacer un ejemplo más completo usando sys.exit() que es usado para finalizar el programa/script. Para ello vamos hacer una super simple… llamémosle… ¿calculadora? Que solo suma números y le daremos la opción al usuario de salir o continuar con el programa.
import sys # Calculadora que solo suma while True: # Pedimos dos numeros num1 = float(input("Ingresa el primer número: ")) num2 = float(input("Ingresa el segundo número: ")) # Mostrar el resultado de la suma print(f"El resultado de {num1} + {num2} es: {num1 + num2}") # Preguntar si quiere continuar o salir respuesta = input("¿Quieres realizar otra suma? (s/n): ").strip().lower() if respuesta != 's': print("¡Nos vemos! ;-)") sys.exit() # Salimos
El ejemplo como digo es bastante sencillo y creo que no merece una explicación, básicamente le pedimos al usuario dos números y los sumamos, pero como he dicho antes, hacemos uso de sys.exit() para darle la opción al usuario de salir o no.
¿Se entiende no? 😜
Sigamos…
Puede que nos interese que nos muestre el sistema operativo donde está corriendo el programa:
print("Sistema operativo:", sys.platform)
Y ahora, seguro que te preguntas, ¿pero no hace lo mismo os.name como hemos visto al principio del articulo?
La respuesta es sí, os.name y sys.platform se utilizan para identificar el sistema operativo en el que se ejecuta un programa, correcto, pero tienen diferencias algunas diferencias clave en el nivel de detalle y cómo representan la información.
Me explico, os.name, devuelve el núcleo (kernel) del sistema operativo. Si estamos usando “Linux” por ejemplo, nos devolverá “POSIX” (que hace referencia a sistemas basados en POSIX) como son Linux, Unix, macOS.
Si ejecutamos os.name en un sistema Windows, nos devolverá “nt”.
Como ves, es muy básico y no distingue entre diferentes versiones o variantes del sistema operativo. Por ejemplo, no puede diferencia entre Linux y macOS (ya que ambos aparecen como «POSIX»).
Sin embargo, sys.platform, devuelve información más detallada que identifica el sistema operativo y, en algunos casos, la versión.
Por ejemplo, si ejecutamos sys.platform en Windows nos devolverá “win32”. Si estamos en un macOS nos devolverá “Darwin”, si estamos en Linux nos devolverá “Linux”… Como ves, sys.platform es más específico que os.name y distingue entre variantes del mismo núcleo. Por ejemplo, diferencia entre «linux» y «darwin» (que ambos son «posix» en os.name).
Por ejemplo, podemos usar ambos para tener más precisión y decidir que hacer dependiendo del sistema operativo:
import os import sys if os.name == "nt": print("Estás en Windows.") elif os.name == "posix": if sys.platform == "darwin": print("Estás en macOS.") elif sys.platform == "linux": print("Estás en Linux.") else: print("Sistema basado en POSIX desconocido.") else: print("Sistema operativo no reconocido.")
También podemos hacer uso de sys.path que contiene una lista con las rutas donde Python busca los módulos cuando usamos import y agregar una nueva ruta personalizada para que busque ahí.
Por ejemplo:
import sys # Mostrar las rutas de búsqueda actuales print("Rutas de búsqueda actuales:") for ruta in sys.path: print(ruta) # Añadir una nueva ruta sys.path.append("/ruta/modulos_propios") print("\nNueva ruta añadida:", sys.path[-1])
En definitiva, como ves, el módulo sys resulta bastante útil para crear scripts o programas interactivos que podemos usar desde la línea de comandos, así como modificar el entorno del interprete cambiando la ruta de búsqueda de modulos como acabamos de hacer en el último ejemplo.
Modulo datetime
El módulo datetime nos permite trabajar con fechas y horas, incluyendo realizar operaciones matemáticas con las mismas, aunque como dice la documentación oficial, el principal objetivo y uso es poder extraer campos (fechas y horas) de forma eficiente para su posterior manipulación o formateo.
Veamos algunos ejemplos.
Si queremos conocer la fecha y hora actual:
from datetime import datetime fecha_hora = datetime.now() print("Fecha y hora actual:", fecha_hora)
El resultado en mi caso (mientras escribo el borrador de este artículo 😅):
Fecha y hora actual: 2024-12-13 16:22:58.992885
Para mostrarla un poco mejor podemos formatearla usando strftime:
# Formateo de fecha print("Fecha formateada:", fecha_hora.strftime("%d/%m/%Y %H:%M:%S"))
El resultado será: 13/12/2024 16:28:08
¿Algo mejor no?
Por cierto, el formateo que podemos usar es:
- %d: Representa el día del mes (01-31)
- %m: Representa el mes (01-12)
- %Y: Representa un año completo
- %H: Representa la hora en formato 24h (00-23)
- %M: Representa los minutos (00-59)
- %S: Representa los segundos (00-59)
Respecto a las operaciones que podemos realizar con las fechas son, por ejemplo, sumar o restar días, calcular un intervalo de días, etc.
Sumando y restando días.
Importamos la clase timedelta del módulo datetime
Y sumamos:
from datetime import timedelta # Sumar 5 días a la fecha actual futura = fecha_hora + timedelta(days=5) print("Fecha futura:", futura)
O restamos:
from datetime import timedelta # Restar 5 días pasada = fecha_hora - timedelta(days=5) print("Fecha pasada:", pasada)
También si nos interesa calcular la diferencia de días que hay entre dos fechas, podemos hacer:
# Calcular diferencia de días: fecha1 = datetime(2024, 12, 1) fecha2 = datetime(2024, 12, 25) diferencia = fecha2 - fecha1 print("Diferencia en días:", diferencia.days)
El resultado es: Diferencia en días: 24
Comparando fechas
Para comparar fechas, por ejemplo, si una es mayor que otra, menor, igual, etc. Tan solo debemos de usar los operadores de comparación: > (mayor que), < (menor que), == (igual que), etc.
# Comparando fechas fecha1 = datetime(2024, 12, 1) fecha2 = datetime(2024, 12, 25) if fecha1 < fecha2: print(f"{fecha1.strftime('%d/%m/%Y')} es anterior a {fecha2.strftime('%d/%m/%Y')}") else: print(f"{fecha1.strftime('%d/%m/%Y')} es posterior o igual a {fecha2.strftime('%d/%m/%Y')}")
¿Fácil no?
No parece complicado trabajar con fechas 😉
Módulo math
El módulo math es otro módulo que forma parte la biblioteca estándar de Python. Proporciona funciones matemáticas avanzadas que no están disponibles de forma directa en el lenguaje.
Nos ofrece la posibilidad de realizar cálculos más allá de las operaciones básicas como suma, resta, multiplicación y división.
Podemos realizar cálculos logarítmicos, exponenciales, redondeos y muchos más.
Un ejemplo sencillo para probar este módulo, es calcular el número o representar el infinito:
print("Pi:", math.pi) print("Infinito:", math.inf)
Después, encontramos funciones más avanzadas para cálculos matemáticos más avanzados (valga la redundancia).
Potencias y raíces cuadradas
import math # Potencia print("5 elevado a 2:", math.pow(5, 2)) # Raíz cuadrada print("Raíz cuadrada de 16:", math.sqrt(16))
Logaritmos y exponenciales
# Exponencial print("e^2:", math.exp(2)) # Logaritmos print("Logaritmo base 10 de 1000:", math.log10(1000))
Factorial
# Factorial print("6! (factorial):", math.factorial(6))
Redondeo
Podemos redondear números hacia arriba, abajo o al entero más cercano:
# Redondear hacia arriba print("Redondeo hacia arriba de 2.6:", math.ceil(2.6)) # 3 # Redondear hacia abajo print("Redondeo hacia abajo de 2.9:", math.floor(2.9)) # 2 # Valor absoluto print("Valor absoluto de -5:", math.fabs(-5)) # 5.0
Trigonometría
Tenemos también algunas funciones trigonométricas como seno, coseno y tangente.
# Trigonometría básica print("Seno de pi/2:", math.sin(math.pi / 2)) # 1.0 print("Coseno de 0:", math.cos(0)) # 1.0 print("Tangente de pi/4:", math.tan(math.pi / 4)) # 1.0
Modulo random
El módulo random es un módulo bastante útil y esencial que permite generar números aleatorios o realizar operaciones aleatorias como mostrar elementos de una lista al azar, por ejemplo.
El uso más simple es:
print(random.random())
Que genera un número decimal entre 0.0 y 1.0.
Por otro lado, otro ejemplo sencillo es generar un número entre a y b:
import random print(random.randint(1, 100)) # Genera un número entre 1 y 100
Y también, como he dicho antes, también es útil para mostrar algún elemento de una lista al azar.
Esto lo conseguimos usando el método choice de la clase random:
frutas = ["melón", "sandía", "fresa", "plátano"] seleccion = random.choice(frutas) print("Elección aleatoria:", seleccion)
Cada vez que lo ejecutemos mostrará una fruta.
Ahora si queremos que en vez de mostrar una fruta las muestre todas de forma aleatoria cada vez que se ejecute el código, debemos de hacer una ligera modificación y usar el método shuffle():
frutas = ["melón", "sandía", "fresa", "plátano"] random.shuffle(frutas) print("Fruta mezclada:", frutas)
Posible salida: «Fruta mezclada: [‘sandía’, ‘plátano’, ‘fresa’, ‘melón’]»
Generar una contraseña
Aquí nos encontramos con un «pequeño inconveniente» que no todo el mundo conoce y, aunque puedes generar contraseñas con el módulo random, resulta que no es tan seguro para aplicaciones donde realmente se necesita una gran aleatoriedad criptográfica.
Esto se debe a que los números generados con random, son pseudoaleatorios y predecibles.
En un próximo articulo lo explicare con más detalle, ya que ahora se saldría un poco de los objetivos de este articulo y lo haríamos demasiado largo (más de lo que es 😅), así que quédate con eso, ¿puedes generar contraseñas con random? Sí, pero no del todo seguras.
Un ejemplo de generación de una contraseña usando dicho módulo es el siguiente:
import random import string # Tamaño de la contraseña longitud = 10 # Conjunto de caracteres permitidos (letras, numeros y caracteres especiales como @, #, ?, etc.) caracteres = string.ascii_letters + string.digits + string.punctuation # Generar la contraseña contraseña = ''.join(random.choice(caracteres) for _ in range(longitud)) print("Contraseña generada:", contraseña)
Primero, aparte de importar el módulo random, también importamos el módulo string, que nos proporciona conjuntos de caracteres, como letras y dígitos.
Mediante la línea:
caracteres = string.ascii_letters + string.digits + string.punctuation
- string.ascii_letters: Contiene todas las letras (tanto mayúsculas y minúsculas de la aA-zZ).
- string.digits: Contiene los números del 0 al 9.
- string.punctuation: Contiene caracteres especiales como @, #, ?, (, etc.
Concatenamos estos conjuntos para tener un rango amplio de caracteres.
Por último, con:
contraseña = ''.join(random.choice(caracteres) for _ in range(longitud))
Hacemos uso de random.choice(), para seleccionar un carácter aleatorio del conjunto caracteres.
También usamos la comprensión de listas (for _ in range(longitud)), para iterar tantas veces (como marque longitud) para generar una lista de caracteres aleatorios.
Y con ».join(…), unimos todos los caracteres generados (los 10) en una sola cadena, generando así la contraseña.
Por ejemplo: d}T.+$l2;Q
Modulo collections
El módulo collections, es otro módulo útil que proporciona estructuras de datos avanzadas ampliando las funcionalidades de las colecciones ya integradas como son las listas, diccionarios y tuplas.
Las colecciones estándar de Python (listas, tuplas y diccionarios) son muy útiles, pero en ciertos casos tienen limitaciones o hacen que tu código llegue a ser más complejo. El módulo collections proporciona estructuras diseñadas específicamente para resolver problemas comunes de una forma más eficiente y con un código más claro.
Por ejemplo, tenemos entre otras, la clase Counter que permite el “conteo” de elementos de una colección.
Supón el siguiente ejemplo: tienes un diccionario y quieres contar los elementos del mismo.
Sin usar Counter harías así:
cesta = ["melón", "sandía", "manzana", "pera", "manzana", "sandía", "plátano", "ciruela"] conteo = {} for producto in cesta: if producto in conteo: conteo[producto] += 1 else: conteo[producto] = 1 # Cuantas frutas hay? print(conteo)
Dando como resultado:
{‘melón’: 1, ‘sandía’: 2, ‘manzana’: 2, ‘pera’: 1, ‘plátano’: 1, ‘ciruela’: 1}
Sin embargo, podemos hacerlo más fácil usando el módulo collections, concretamente la clase Counter de dicho módulo, que extiende las funcionalidades de un diccionario estándar para facilitar el conteo de elementos.
from collections import Counter cesta = ["melón", "sandía", "manzana", "pera", "manzana", "sandía", "plátano", "ciruela"] conteo = Counter(cesta) print(conteo)
¿Ves la diferencia?
Evidentemente el código es mucho más claro y legible.
Pues bien, el módulo collections aparte de la clase Counter, tiene otras como: deque: (doble-ended queue), defaultdict, namedtuple, etc.
deque
deque (doble-ended queue) es como una lista optimizada (con appends y pops rápidos) para añadir y eliminar elementos rápidamente en ambos extremos.
from collections import deque frutas = deque(["melón", "sandía", "plátano"]) frutas.append("melocotón") # Añadir al final print(frutas) frutas.popleft() # Eliminar el primer elemento print(frutas)
defaultdict
Diccionario que devuelve un valor predeterminado cuando intentas acceder a una clave que no existe. Esto evita errores como KeyError.
from collections import defaultdict # Crear un defaultdict con valores predeterminados de tipo entero cesta = defaultdict(int) # Añadir elementos al inventario cesta["manzanas"] += 10 cesta["peras"] += 5 # Intentar acceder a una clave que no existe print("Cantidad de manzanas:", cesta["manzanas"])
Ahora, que ocurre si algún elemento de la cesta no existe, es decir, si hacemos así:
print("Cantidad de naranjas:", cesta["naranjas"])
Pues en lugar de mostrar un error (KeyError) mostraría:
Cantidad de naranjas: 0
namedtuple
Igual que una tupla normal, pero con nombres para acceder a los elementos por nombre en lugar de índices (aunque también puedes seguir haciéndolo por índices).
Por ejemplo, una tupla normal puede ser la siguiente:
# Tupla normal: alumnos = ("Oscar", 36, "Maria", 30, "Paula", 32) print(alumnos[0]) # Acceso por índice
Y nos mostraría “Oscar”.
Pero si ahora, hacemos el código más legible usando la clase “namedtuple”:
from collections import namedtuple # Crear la namedtuple Persona = namedtuple("Persona", ["nombre", "edad"]) # Crear una lista para almacenar varias personas personas = [] # Agregar personas a la lista personas.append(Persona(nombre="Oscar", edad=36)) personas.append(Persona(nombre="Maria", edad=30)) personas.append(Persona(nombre="Paula", edad=32)) # Recorrer la lista y mostrar las personas for persona in personas: print(f"Nombre: {persona.nombre}, Edad: {persona.edad}")
¿Ves? Accedemos a los elementos a través del nombre del campo en lugar del índice, de esta forma no dejamos de usar una tupla, pero esta vez es mucho más legible.
No solo tenemos más funciones o clases en este módulo, sino que con todos los que hemos visto hasta ahora, podemos hacer muchas más cosas, pero como es obvio, ver todas las opciones que tenemos disponibles o todos los usos que le podemos dar a cada módulo, se saldría de los objetivos de este articulo aparte de que sería un artículo infinito.
Este solo ha sido, en mi opinión, un acercamiento a los 6 módulos útiles en Python, sobre todo al principio.
Para más información sobre cada módulo te invito a leer la documentación oficial.
Por mi parte, esto ha sido todo. Espero que no se te haya hecho muy largo 🙏 y te sirva de utilidad.
¡Un saludo! 😉