Módulos y la biblioteca estándar
Python viene con una enorme colección de herramientas listas para usar: aleatoriedad, matemáticas, fechas, rutas de archivos y mucho más. Estas herramientas viven en módulos, y los traes a tu código con import. Ya usaste import json en el capítulo anterior. Este capítulo cubre los imports en profundidad y presenta las partes más útiles de la biblioteca estándar.
Importar módulos
El import más simple trae un módulo completo y te permite usar su contenido con notación de punto. También puedes importar nombres específicos de un módulo para usarlos directamente sin el prefijo. Los alias acortan nombres largos.
import math
math.sqrt(16) # 4.0
math.pi # 3.141592653589793
math.floor(3.9) # 3
math.ceil(3.1) # 4Importa nombres específicos de un módulo para usarlos directamente:
from math import sqrt, pi
sqrt(16) # 4.0 (no se necesita el prefijo "math.")
pi # 3.141592653589793Asigna un alias a un módulo o nombre para acortarlo:
import math as m
m.sqrt(16) # 4.0
from math import sqrt as square_root
square_root(25) # 5.0Los alias son comunes con bibliotecas populares de terceros (import numpy as np, import pandas as pd). Para los módulos de la biblioteca estándar, prefiere usar el nombre completo; hace el código más fácil de leer.
random
El módulo random genera números aleatorios y hace elecciones aleatorias. Úsalo para juegos, simulaciones, muestreo aleatorio y cualquier otra cosa que necesite imprevisibilidad. Establecer una semilla (seed) hace que los resultados sean reproducibles: la misma semilla produce la misma secuencia cada vez.
import random
random.random() # float entre 0 y 1 (exclusivo)
random.randint(1, 10) # entero del 1 al 10 (ambos inclusive)
random.uniform(1.0, 10.0) # float entre 1.0 y 10.0
colours = ["red", "green", "blue"]
random.choice(colours) # elige un elemento
random.choices(colours, k=3) # elige k elementos (con reemplazo)
random.sample(colours, k=2) # elige k elementos (sin reemplazo)
numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers) # mezcla in situ, devuelve NonePara resultados reproducibles (útil en pruebas y ciencia de datos), establece una semilla antes de generar:
random.seed(42)
random.randint(1, 100) # siempre el mismo valor para la semilla 42La misma semilla produce la misma secuencia cada vez, en cualquier máquina.
math
El módulo math agrega operaciones matemáticas más avanzadas más allá de los operadores aritméticos básicos. Raíces cuadradas, potencias, logaritmos, trigonometría y valores especiales como pi e infinito están todos aquí.
import math
math.sqrt(25) # 5.0
math.pow(2, 10) # 1024.0 (igual que 2 ** 10 pero siempre devuelve float)
math.log(100, 10) # 2.0 (logaritmo base 10)
math.log(math.e) # 1.0 (logaritmo natural)
math.sin(math.pi / 2) # 1.0
math.cos(0) # 1.0
math.ceil(3.2) # 4
math.floor(3.9) # 3
math.trunc(3.9) # 3 (igual que int() para positivos)
math.inf # infinito
math.isnan(float("nan")) # True
math.isinf(math.inf) # Truedatetime
El módulo datetime maneja fechas y horas. datetime.now() te da la fecha y hora actuales. strftime() lo formatea como cadena. strptime() parsea una cadena a datetime. timedelta representa una duración que puedes sumar o restar.
from datetime import datetime, date, timedelta
now = datetime.now() # fecha y hora actuales
today = date.today() # solo fecha actual
print(now.year, now.month, now.day)
print(now.hour, now.minute, now.second)
# Formateo
print(now.strftime("%Y-%m-%d")) # "2024-01-15"
print(now.strftime("%d %B %Y, %H:%M")) # "15 January 2024, 09:42"
# Parseo
deadline = datetime.strptime("2024-12-31", "%Y-%m-%d")
# Aritmética
tomorrow = today + timedelta(days=1)
next_week = today + timedelta(weeks=1)
diff = deadline - now
print(f"{diff.days} days until deadline")Códigos comunes de strftime:
| Código | Significado | Ejemplo |
|---|---|---|
%Y | Año de 4 dígitos | 2024 |
%m | Mes (con cero a la izquierda) | 01 |
%d | Día (con cero a la izquierda) | 15 |
%H | Hora (24h) | 09 |
%M | Minuto | 42 |
%B | Nombre completo del mes | January |
os y pathlib
pathlib es la forma moderna de trabajar con rutas de archivos. Los objetos Path te permiten construir, inspeccionar y navegar rutas usando el operador /. os da acceso a variables de entorno y operaciones de sistema operativo de más bajo nivel. Prefiere pathlib para código nuevo.
from pathlib import Path
p = Path("data/reports")
p.exists() # True si la ruta existe
p.is_dir() # True si es un directorio
p.is_file() # True si es un archivo
p.mkdir(parents=True, exist_ok=True) # crea directorios
for f in p.glob("*.csv"): # todos los archivos CSV del directorio
print(f.name) # solo el nombre del archivo
report = p / "report_jan.csv" # el operador / une rutas
report.stem # "report_jan" (nombre sin extensión)
report.suffix # ".csv"
report.parent # Path("data/reports")
content = report.read_text() # lee el contenido del archivo directamente
report.write_text("new content\n") # escribe directamentePara el módulo os:
import os
os.getcwd() # directorio de trabajo actual
os.listdir(".") # lista el contenido del directorio
os.path.exists("data.txt") # True si la ruta existe
os.path.join("data", "file.txt") # "data/file.txt" (multiplataforma)
os.environ.get("HOME") # lee una variable de entornoPrefiere pathlib para código nuevo. Usa os cuando necesites variables de entorno o trabajar con APIs antiguas que esperan cadenas.
timeit
timeit mide cuánto tarda en ejecutarse el código. Es útil cuando quieres comparar dos enfoques y elegir el más rápido. Ejecuta el código muchas veces para obtener una medición estable.
import timeit
# Cronometrar una sola sentencia
timeit.timeit("sum(range(1000))", number=10000)
# Cronometrar un bloque más complejo
setup = "data = list(range(1000))"
code = "[x * 2 for x in data]"
time = timeit.timeit(code, setup=setup, number=10000)
print(f"{time:.4f} seconds for 10,000 runs")number es la cantidad de veces que se repite. Más repeticiones dan una medición más estable.
string
El módulo string proporciona constantes de cadena predefinidas para letras, dígitos y signos de puntuación. Útil cuando necesitas verificar caracteres o generar cadenas aleatorias a partir de un alfabeto específico.
import string
string.ascii_lowercase # "abcdefghijklmnopqrstuvwxyz"
string.ascii_uppercase # "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
string.ascii_letters # ambas combinadas
string.digits # "0123456789"
string.punctuation # todos los caracteres de puntuaciónÚtil cuando necesitas verificar caracteres o generar cadenas aleatorias:
import string, random
chars = string.ascii_letters + string.digits
password = "".join(random.choices(chars, k=12))Crear tus propios módulos
Cualquier archivo de Python es un módulo. Para usarlo desde otro archivo, impórtalo por el nombre del archivo (sin .py). Puedes importar el módulo completo y usar su contenido con notación de punto, o importar nombres específicos directamente.
# utils.py
def clamp(value, lo, hi):
return max(lo, min(value, hi))
PI = 3.14159# main.py
import utils
utils.clamp(150, 0, 100) # 100
utils.PI # 3.14159
from utils import clamp
clamp(50, 0, 100) # 50Python encuentra el módulo buscando en el mismo directorio que el archivo que lo importa (y algunos otros lugares). Para proyectos más grandes, los módulos se organizan en paquetes: directorios con un archivo __init__.py.
__name__ == "__main__"
Cuando Python ejecuta un archivo directamente, __name__ se establece en "__main__". Cuando el mismo archivo se importa como módulo, __name__ es el nombre del módulo. Este patrón te permite escribir código que se ejecuta cuando ejecutas el archivo directamente pero se omite cuando otro módulo importa el archivo.
# utils.py
def clamp(value, lo, hi):
return max(lo, min(value, hi))
if __name__ == "__main__":
# esto solo se ejecuta cuando haces: python utils.py
# no cuando haces: import utils
print(clamp(150, 0, 100)) # 100Este es un patrón estándar para cualquier módulo que también sea útil como script independiente.
Lo más destacado de la biblioteca estándar
Algunos módulos más que vale la pena conocer. Cada uno resuelve un problema común que llevaría un trabajo significativo implementar tú mismo.
collections: tipos de contenedores especializados:
from collections import Counter, defaultdict, deque
Counter(["a", "b", "a", "c", "a"]) # Counter({'a': 3, 'b': 1, 'c': 1})
defaultdict(list) # dict que crea claves faltantes automáticamente
deque([1, 2, 3], maxlen=5) # append/pop rápido por ambos extremositertools: herramientas para trabajar con iterables:
import itertools
list(itertools.chain([1, 2], [3, 4])) # [1, 2, 3, 4]
list(itertools.islice(range(100), 5)) # [0, 1, 2, 3, 4]
list(itertools.combinations([1, 2, 3], 2)) # [(1, 2), (1, 3), (2, 3)]
list(itertools.product([0, 1], repeat=2)) # [(0,0), (0,1), (1,0), (1,1)]sys: acceso al intérprete de Python:
import sys
sys.argv # lista de argumentos de línea de comandos
sys.exit(1) # sale con un código de estado
sys.version # cadena con la versión de PythonPaquetes de terceros: más allá de la biblioteca estándar, pip instala paquetes de la comunidad:
pip install requests # biblioteca HTTP
pip install pandas # manipulación de datos
pip install numpy # computación numéricaLos paquetes de terceros están fuera del alcance de esta guía, pero el patrón siempre es el mismo: pip install, luego import.
En la práctica
Combinando random, string y datetime para generar IDs únicos de partida con marcas de tiempo:
import random
import string
from datetime import datetime
def generate_game_id(length: int = 8) -> str:
chars = string.ascii_uppercase + string.digits
return "".join(random.choices(chars, k=length))
def timestamp() -> str:
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
game_id = generate_game_id()
print(f"[{timestamp()}] Starting game {game_id}")
scores = [random.randint(50, 100) for _ in range(5)]
print(f"Round scores: {scores}")
print(f"Best: {max(scores)}")
