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 las traes a tu código con import. Ya usaste import json en el capítulo de Archivos y excepciones. Este capítulo cubre los imports en detalle e introduce las partes más útiles de la biblioteca estándar.
Importar módulos
El import más simple trae todo un módulo 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 abrevian 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 hace falta el prefijo "math.")
pi # 3.141592653589793Dale un alias a un módulo o nombre para abreviarlo:
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 de terceros populares (import numpy as np, import pandas as pd). Para los módulos de la biblioteca estándar, prefiere usar el nombre completo; hace que el código sea más legible.
import math trae todo el módulo, y luego accedes con un punto: math.sqrt(16). from math import sqrt toma un solo nombre para que puedas prescindir del prefijo. La palabra clave as renombra un import largo, pero para cosas de la biblioteca estándar el nombre completo se lee más claro. random
El módulo random genera números aleatorios y hace selecciones al azar. Úsalo para juegos, simulaciones, muestreo aleatorio, y cualquier otra cosa que necesite impredecibilidad. Establecer una semilla 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 entre 1 y 10 (ambos inclusive)
random.uniform(1.0, 10.0) # float entre 1.0 y 10.0
colores = ["rojo", "verde", "azul"]
random.choice(colores) # elige un elemento
random.choices(colores, k=3) # elige k elementos (con reemplazo)
random.sample(colores, k=2) # elige k elementos (sin reemplazo)
numeros = [1, 2, 3, 4, 5]
random.shuffle(numeros) # mezcla en el lugar, 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.
random.choice() elige un elemento de una lista, random.randint(1, 10) da un número entero en un rango, con el extremo incluido. ¿Quieres los mismos resultados en cada ejecución, digamos para una prueba? Llama a random.seed() primero y la secuencia queda fija. math
El módulo math agrega operaciones matemáticas más avanzadas más allá de los operadores aritméticos básicos que viste en el capítulo de Números. Raíces cuadradas, potencias, logaritmos, trigonometría, y valores especiales como pi e infinito, todo está 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 (log base 10)
math.log(math.e) # 1.0 (log 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) # Truemath cubre todo lo que va más allá de + - * /: raíces cuadradas, potencias, logaritmos, trigonometría, además de constantes como math.pi. Recurre a él en el momento en que un cálculo necesite más que los operadores básicos, y te ahorras escribir tú mismo la fórmula. datetime
El módulo datetime maneja fechas y horas. datetime.now() te da la fecha y hora actuales. strftime() lo formatea como una cadena de texto. strptime() parsea una cadena de texto para convertirla en un 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 la 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 cifras | 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 |
datetime.now() te da la fecha y hora actuales. strftime() lo convierte en una cadena de texto con códigos como %Y-%m-%d, y strptime() hace el camino inverso, parseando una cadena de texto de vuelta. timedelta es una duración que puedes sumar o restar, así que today + timedelta(days=1) es mañana. 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 te da acceso a variables de entorno y operaciones de más bajo nivel del sistema operativo. 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 los directorios
for f in p.glob("*.csv"): # todos los archivos CSV del directorio
print(f.name) # 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 más antiguas que esperan cadenas de texto.
pathlib.Path trata una ruta de archivo como un objeto que puedes explorar: .exists(), .read_text(), .write_text(), .glob(). El operador / une piezas, así que p / "report.csv" se lee como una ruta real. Recurre a pathlib por sobre el más antiguo os.path en código nuevo. timeit
timeit mide cuánto tarda en ejecutarse un 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
# Cronometra una sola instrucción
timeit.timeit("sum(range(1000))", number=10000)
# Cronometra 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 cuántas veces se repite. Más repeticiones dan una medición más estable.
timeit.timeit() cronometra un fragmento de código ejecutándolo una y otra vez, y luego divides por la cantidad de ejecuciones para obtener un número estable por llamada. Así es como resuelves "cuál de estos dos es más rápido" en lugar de adivinar. Una sola ejecución es demasiado ruidosa como para confiar en ella, así que déjalo repetir. string
El módulo string ofrece constantes de texto ya construidas para letras, dígitos y puntuación. Útil cuando necesitas verificar caracteres o generar cadenas de texto aleatorias a partir de un alfabeto específico.
import string
string.ascii_lowercase # "abcdefghijklmnopqrstuvwxyz"
string.ascii_uppercase # "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
string.ascii_letters # ambos combinados
string.digits # "0123456789"
string.punctuation # todos los caracteres de puntuaciónÚtil cuando necesitas verificar caracteres o generar cadenas de texto aleatorias:
import string, random
chars = string.ascii_letters + string.digits
password = "".join(random.choices(chars, k=12))string te da conjuntos de caracteres ya armados: string.ascii_letters, string.digits, string.punctuation. Son cadenas de texto normales, así que puedes recorrerlas o tomar muestras de ellas. Útil cada vez que necesitas un alfabeto conocido para construir o verificar contra él. 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 todo el módulo 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.
.py ya es un módulo: impórtalo por su nombre de archivo sin el .py, y sus funciones y variables quedan a tu disposición. import utils y luego utils.clamp(...), o toma un solo nombre con from utils import clamp. Así es como divides un programa que va creciendo en varios archivos. __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 corres el archivo directamente, pero que se omite cuando otro módulo lo importa.
# utils.py
def clamp(value, lo, hi):
return max(lo, min(value, hi))
if __name__ == "__main__":
# esto solo se ejecuta cuando corres: python utils.py
# no cuando corres: 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.
if __name__ == "__main__": permite que un archivo sea a la vez un módulo importable y un script ejecutable. El código bajo la protección se ejecuta cuando corres python utils.py, y se omite cuando otro archivo hace import utils. Útil para meter una demostración o prueba rápida al final de un módulo. 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 implementar por tu cuenta te tomaría bastante trabajo.
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 automáticamente las claves faltantes
deque([1, 2, 3], maxlen=5) # append/pop rápido en 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 la línea de comandos
sys.exit(1) # sale con un código de estado
sys.version # cadena de texto 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 # cómputo numéricoLos paquetes de terceros quedan fuera del alcance de esta guía, pero el patrón siempre es el mismo: pip install, y 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)}")
