39. Módulos esenciales de la biblioteca estándar
La biblioteca estándar de Python es una colección de módulos que vienen integrados con Python: no necesitas instalar nada extra para usarlos. Estos módulos proporcionan herramientas potentes para tareas comunes de programación: generar números aleatorios, trabajar con fechas y horas, intercambiar datos con otros programas y usar estructuras de datos especializadas que van más allá de las listas y diccionarios básicos.
En este capítulo, exploraremos cinco módulos esenciales de la biblioteca estándar que usarás con frecuencia en la programación Python del mundo real.
39.1) Generar aleatoriedad con random
El módulo random proporciona funciones para generar números aleatorios y hacer selecciones aleatorias. Esto es útil para simulaciones, juegos, pruebas, muestreo de datos y cualquier situación en la que necesites un comportamiento impredecible.
39.1.1) Generar enteros aleatorios con randint()
La función randint() genera un entero aleatorio entre dos valores, incluyendo ambos extremos:
import random
# Simular tirar un dado de seis caras
die_roll = random.randint(1, 6)
print(f"You rolled: {die_roll}") # Output: You rolled: 4 (varies each run)
# Generar una edad aleatoria entre 18 y 65
age = random.randint(18, 65)
print(f"Random age: {age}") # Output: Random age: 42 (varies)Fíjate en que tanto el valor inicial como el final se incluyen en los resultados posibles. randint(1, 6) puede devolver 1, 2, 3, 4, 5 o 6: los seis valores son posibles.
Aquí tienes un ejemplo práctico que simula varias tiradas de dados:
import random
# Simular tirar dos dados y calcular su suma
die1 = random.randint(1, 6)
die2 = random.randint(1, 6)
total = die1 + die2
print(f"Die 1: {die1}") # Output: Die 1: 3 (varies)
print(f"Die 2: {die2}") # Output: Die 2: 5 (varies)
print(f"Total: {total}") # Output: Total: 8 (varies)
if total == 7:
print("Lucky seven!")
elif total == 2 or total == 12:
print("Snake eyes or boxcars!")Por qué ambos extremos son inclusivos: Esto hace que randint() sea intuitivo para casos de uso comunes. Cuando quieres un número del 1 al 6 (como un dado), escribes randint(1, 6) y tanto 1 como 6 son resultados posibles.
39.1.2) Generar números aleatorios de punto flotante
Para números decimales aleatorios, usa random() (devuelve un float entre 0.0 y 1.0) o uniform() (devuelve un float entre dos valores especificados):
import random
# Generar un float aleatorio entre 0.0 y 1.0 (0.0 incluido, 1.0 excluido)
probability = random.random()
print(f"Random probability: {probability:.4f}") # Output: Random probability: 0.7284 (varies)
# Generar una temperatura aleatoria entre 15.0 y 30.0 grados
temperature = random.uniform(15.0, 30.0)
print(f"Temperature: {temperature:.2f}°C") # Output: Temperature: 23.47°C (varies)
# Generar un precio aleatorio entre $10.00 y $99.99
price = random.uniform(10.0, 99.99)
print(f"Price: ${price:.2f}") # Output: Price: $45.67 (varies)La función random() es útil cuando necesitas un valor de probabilidad o un porcentaje. La función uniform() es mejor cuando necesitas un decimal aleatorio dentro de un rango específico.
39.1.3) Hacer elecciones aleatorias con choice()
La función choice() selecciona aleatoriamente un elemento de una secuencia (lista(list), tupla(tuple) o cadena(string)):
import random
# Seleccionar un color al azar
colors = ["red", "blue", "green", "yellow", "purple"]
selected_color = random.choice(colors)
print(f"Selected color: {selected_color}") # Output: Selected color: green (varies)
# Seleccionar un ganador al azar entre participantes
participants = ["Alice", "Bob", "Charlie", "Diana"]
winner = random.choice(participants)
print(f"The winner is: {winner}") # Output: The winner is: Bob (varies)
# Seleccionar un carácter al azar de una cadena
vowels = "aeiou"
random_vowel = random.choice(vowels)
print(f"Random vowel: {random_vowel}") # Output: Random vowel: i (varies)Esto es particularmente útil para juegos, muestreo aleatorio o selección de datos de prueba aleatorios. Cada elemento en la secuencia tiene la misma probabilidad de ser elegido.
Aquí tienes un ejemplo más complejo que simula un juego de preguntas sencillo:
import random
# Preguntas del cuestionario con sus respuestas
questions = [
("What is 2 + 2?", "4"),
("What is the capital of France?", "Paris"),
("What color is the sky?", "blue")
]
# Seleccionar una pregunta al azar
question, correct_answer = random.choice(questions)
print(f"Question: {question}")
user_answer = input("Your answer: ")
if user_answer.lower() == correct_answer.lower():
print("Correct!")
else:
print(f"Wrong! The answer was: {correct_answer}")39.1.4) Seleccionar varios elementos aleatorios con sample()
Cuando necesitas seleccionar varios elementos únicos de una secuencia, usa sample(). Es como sacar cartas de una baraja sin reemplazo: una vez se selecciona un elemento, no se seleccionará de nuevo:
import random
# Seleccionar 3 estudiantes al azar para un proyecto en grupo
students = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
group = random.sample(students, 3)
print(f"Group members: {group}") # Output: Group members: ['Diana', 'Alice', 'Frank'] (varies)
# Sacar 5 números de lotería del 1 al 50 (sin duplicados)
lottery_numbers = random.sample(range(1, 51), 5)
lottery_numbers.sort() # Ordenar para mostrar
print(f"Lottery numbers: {lottery_numbers}") # Output: Lottery numbers: [7, 15, 23, 38, 49] (varies)El segundo argumento de sample() especifica cuántos elementos seleccionar. El número debe ser menor o igual que la longitud de la secuencia: no puedes seleccionar más elementos de los que hay disponibles.
39.1.5) Barajar secuencias con shuffle()
La función shuffle() reordena aleatoriamente los elementos de una lista(list) in place (modificando la lista original):
import random
# Barajar una baraja de cartas
cards = ["A♠", "K♠", "Q♠", "J♠", "10♠", "9♠", "8♠", "7♠"]
print(f"Original: {cards}")
random.shuffle(cards)
print(f"Shuffled: {cards}") # Output: Shuffled: ['Q♠', '7♠', 'A♠', '10♠', '9♠', 'J♠', 'K♠', '8♠'] (varies)
# Barajar preguntas de un cuestionario para un orden aleatorio
questions = ["Question 1", "Question 2", "Question 3", "Question 4"]
random.shuffle(questions)
print(f"Randomized order: {questions}") # Output: Randomized order: ['Question 3', 'Question 1', 'Question 4', 'Question 2'] (varies)39.2) Trabajar con fechas y horas
El módulo datetime proporciona clases para trabajar con fechas, horas e intervalos de tiempo. Esto es esencial para planificación, logging, cálculo de duraciones y cualquier aplicación que necesite registrar cuándo ocurren las cosas.
39.2.1) Obtener la fecha y hora actuales
La clase datetime representa un punto específico en el tiempo con componentes de fecha y hora:
from datetime import datetime
# Obtener la fecha y hora actuales
now = datetime.now()
print(f"Current datetime: {now}")
# Output: Current datetime: 2026-01-02 14:30:45.123456
# Acceder a componentes individuales
print(f"Year: {now.year}") # Output: Year: 2026
print(f"Month: {now.month}") # Output: Month: 1
print(f"Day: {now.day}") # Output: Day: 2
print(f"Hour: {now.hour}") # Output: Hour: 14
print(f"Minute: {now.minute}") # Output: Minute: 30
print(f"Second: {now.second}") # Output: Second: 45Para solo la fecha (sin hora), usa la clase date:
from datetime import date
# Obtener la fecha de hoy
today = date.today()
print(f"Today: {today}") # Output: Today: 2026-01-02
print(f"Year: {today.year}") # Output: Year: 2026
print(f"Month: {today.month}") # Output: Month: 1
print(f"Day: {today.day}") # Output: Day: 239.2.2) Crear fechas y horas específicas
Puedes crear objetos datetime y date para puntos específicos en el tiempo:
from datetime import datetime, date
# Crear una fecha específica
birthday = date(1995, 7, 15)
print(f"Birthday: {birthday}") # Output: Birthday: 1995-07-15
# Crear un datetime específico
meeting = datetime(2026, 3, 15, 14, 30) # March 15, 2026 at 2:30 PM
print(f"Meeting: {meeting}") # Output: Meeting: 2026-03-15 14:30:00Esto es útil para representar fechas límite, citas, fechas históricas o cualquier punto fijo en el tiempo:
from datetime import date
# Fechas importantes en un proyecto
project_start = date(2026, 1, 15)
project_end = date(2026, 6, 30)
print(f"Project duration: {project_start} to {project_end}")
# Output: Project duration: 2026-01-15 to 2026-06-3039.2.3) Calcular diferencias de tiempo con timedelta
La clase timedelta representa una duración: la diferencia entre dos fechas u horas. Puedes usarla para calcular cuánto tiempo ha pasado o para sumar/restar tiempo a las fechas:
from datetime import date, timedelta
# Calcular la edad
birth_date = date(1995, 7, 15)
today = date(2026, 1, 2)
age_delta = today - birth_date
print(f"Days since birth: {age_delta.days}") # Output: Days since birth: 11128
print(f"Years (approximate): {age_delta.days // 365}") # Output: Years (approximate): 30Cuando restas una fecha de otra, obtienes un objeto timedelta. El atributo days te dice la cantidad de días en esa duración.
También puedes crear objetos timedelta directamente para representar duraciones específicas:
from datetime import date, timedelta
# Sumar días a una fecha
today = date(2026, 1, 2)
one_week = timedelta(days=7)
next_week = today + one_week
print(f"Today: {today}") # Output: Today: 2026-01-02
print(f"Next week: {next_week}") # Output: Next week: 2026-01-09
# Restar días a una fecha
thirty_days_ago = today - timedelta(days=30)
print(f"30 days ago: {thirty_days_ago}") # Output: 30 days ago: 2025-12-03timedelta puede representar días, segundos, microsegundos, milisegundos, minutos, horas y semanas:
from datetime import datetime, timedelta
# Calcular una fecha límite
now = datetime(2026, 1, 2, 14, 30)
deadline = now + timedelta(hours=48, minutes=30)
print(f"Current time: {now}") # Output: Current time: 2026-01-02 14:30:00
print(f"Deadline: {deadline}") # Output: Deadline: 2026-01-04 15:00:00
# Calcular el tiempo restante
time_left = deadline - now
print(f"Hours remaining: {time_left.total_seconds() / 3600}") # Output: Hours remaining: 48.5El método total_seconds() convierte toda la duración a segundos, que luego puedes convertir a horas, minutos o cualquier otra unidad.
Aquí tienes un ejemplo práctico para calcular hitos de un proyecto:
from datetime import date, timedelta
# Planificación del proyecto
project_start = date(2026, 1, 15)
sprint_duration = timedelta(weeks=2)
sprint_1_end = project_start + sprint_duration
sprint_2_end = sprint_1_end + sprint_duration
sprint_3_end = sprint_2_end + sprint_duration
print(f"Sprint 1: {project_start} to {sprint_1_end}")
# Output: Sprint 1: 2026-01-15 to 2026-01-29
print(f"Sprint 2: {sprint_1_end} to {sprint_2_end}")
# Output: Sprint 2: 2026-01-29 to 2026-02-12
print(f"Sprint 3: {sprint_2_end} to {sprint_3_end}")
# Output: Sprint 3: 2026-02-12 to 2026-02-2639.2.4) Comparar fechas y horas
Los objetos date y datetime se pueden comparar usando operadores estándar de comparación:
from datetime import date
# Comparar fechas
date1 = date(2026, 1, 15)
date2 = date(2026, 2, 20)
date3 = date(2026, 1, 15)
print(date1 < date2) # Output: True
print(date1 == date3) # Output: True
print(date2 > date1) # Output: TrueEsto es útil para comprobar fechas límite, validar rangos de fechas y ordenar fechas:
from datetime import date
# Comprobar si una fecha está en el pasado
event_date = date(2025, 12, 25)
today = date(2026, 1, 2)
if event_date < today:
print("This event has already passed") # Output: This event has already passed
else:
print("This event is upcoming")
# Ordenar una lista de fechas
important_dates = [
date(2026, 3, 15),
date(2026, 1, 10),
date(2026, 2, 28)
]
important_dates.sort()
print("Dates in order:") # Output: Dates in order:
for d in important_dates:
print(f" {d}")
# Output:
# 2026-01-10
# 2026-02-28
# 2026-03-1539.2.5) Dar formato a fechas y horas con strftime()
El método strftime() (string format time) convierte fechas y horas en cadenas con formato. Especificas el formato usando códigos especiales:
from datetime import datetime
now = datetime(2026, 1, 2, 14, 30, 45)
# Formatos de fecha comunes
print(now.strftime("%Y-%m-%d")) # Output: 2026-01-02
print(now.strftime("%m/%d/%Y")) # Output: 01/02/2026
print(now.strftime("%B %d, %Y")) # Output: January 02, 2026
print(now.strftime("%A, %B %d, %Y")) # Output: Friday, January 02, 2026
# Formatos de hora comunes
print(now.strftime("%H:%M:%S")) # Output: 14:30:45
print(now.strftime("%I:%M %p")) # Output: 02:30 PM
# Formatos combinados
print(now.strftime("%Y-%m-%d %H:%M:%S")) # Output: 2026-01-02 14:30:45
print(now.strftime("%B %d, %Y at %I:%M %p")) # Output: January 02, 2026 at 02:30 PMCódigos de formato comunes:
| Code | Description | Example |
|---|---|---|
%Y | Año con siglo | 2026 |
%m | Mes como número con cero a la izquierda (01-12) | 01 |
%d | Día como número con cero a la izquierda (01-31) | 02 |
%B | Nombre completo del mes | January |
%b | Nombre corto del mes | Jan |
%A | Nombre completo del día de la semana | Friday |
%a | Nombre corto del día de la semana | Fri |
%H | Hora en formato 24 horas (00-23) | 14 |
%I | Hora en formato 12 horas (01-12) | 02 |
%M | Minuto (00-59) | 30 |
%S | Segundo (00-59) | 45 |
%p | AM/PM | PM |
Aquí tienes un ejemplo práctico creando una entrada de log:
from datetime import datetime
def log_event(message):
"""Registrar un evento con marca de tiempo"""
now = datetime.now()
timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
print(f"[{timestamp}] {message}")
log_event("User logged in")
# Output: [2026-01-02 14:30:45] User logged in
log_event("File uploaded successfully")
# Output: [2026-01-02 14:30:45] File uploaded successfully39.2.6) Analizar fechas desde cadenas con strptime()
La función strptime() (string parse time) convierte cadenas con formato de vuelta a objetos datetime. Especificas los mismos códigos de formato para decirle a Python cómo interpretar la cadena:
from datetime import datetime
# Analizar distintos formatos de fecha
date_str1 = "2026-01-15"
date1 = datetime.strptime(date_str1, "%Y-%m-%d")
print(f"Parsed: {date1}") # Output: Parsed: 2026-01-15 00:00:00
date_str2 = "January 15, 2026"
date2 = datetime.strptime(date_str2, "%B %d, %Y")
print(f"Parsed: {date2}") # Output: Parsed: 2026-01-15 00:00:00
# Analizar datetime con hora
datetime_str = "2026-01-15 14:30:00"
dt = datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
print(f"Parsed: {dt}") # Output: Parsed: 2026-01-15 14:30:00Esto es esencial al leer fechas desde archivos, entrada del usuario o fuentes de datos externas:
from datetime import datetime
# Analizar entrada del usuario
user_input = "03/15/2026"
try:
event_date = datetime.strptime(user_input, "%m/%d/%Y")
print(f"Event scheduled for: {event_date.strftime('%B %d, %Y')}")
# Output: Event scheduled for: March 15, 2026
except ValueError:
print("Invalid date format. Please use MM/DD/YYYY")Importante: La cadena de formato debe coincidir exactamente con la cadena de entrada, o obtendrás un ValueError:
from datetime import datetime
# Esto fallará: el formato no coincide
try:
datetime.strptime("2026-01-15", "%m/%d/%Y") # Wrong format
except ValueError as e:
print(f"Error: {e}")
# Output: Error: time data '2026-01-15' does not match format '%m/%d/%Y'39.3) Leer y escribir datos JSON
JSON (JavaScript Object Notation) es un formato de texto para almacenar e intercambiar datos estructurados. Es el formato más común para APIs web, archivos de configuración e intercambio de datos entre programas. El módulo json de Python facilita convertir entre estructuras de datos de Python y texto JSON.
39.3.1) Comprender la estructura de JSON
JSON se parece a los diccionarios(dict) y listas(list) de Python, pero con algunas diferencias:
JSON admite estos tipos de datos:
- Objetos (como diccionarios(dict) de Python):
{"name": "Alice", "age": 30} - Arrays (como listas(list) de Python):
[1, 2, 3, 4] - Cadenas:
"hello"(debe usar comillas dobles) - Números:
42,3.14 - Booleanos:
true,false(en minúsculas) - Null:
null(comoNonede Python)
Diferencias clave con Python:
- JSON usa
true/false/nullen lugar deTrue/False/Nonede Python - Las cadenas JSON deben usar comillas dobles (
"text"), no comillas simples - JSON no admite tuplas(tuple), conjuntos(set) u objetos personalizados directamente
Así es como se ven los datos JSON:
{
"name": "Alice Johnson",
"age": 30,
"email": "alice@example.com",
"is_active": true,
"scores": [85, 92, 78, 95],
"address": {
"street": "123 Main St",
"city": "Springfield",
"zip": "12345"
}
}Nota: Esto es texto JSON puro, no código Python. Observa el true en minúsculas y el uso de comillas dobles.
39.3.2) Convertir datos de Python a JSON con dumps()
La función dumps() (dump string) convierte estructuras de datos de Python a cadenas con formato JSON:
import json
student = {
"name": "Alice Johnson",
"age": 30,
"email": "alice@example.com",
"is_active": True,
"scores": [85, 92, 78, 95]
}
# Convertir un diccionario a JSON
json_string = json.dumps(student)
print(json_string)
# Output: {"name": "Alice Johnson", "age": 30, "email": "alice@example.com", "is_active": true, "scores": [85, 92, 78, 95]}
print(type(json_string)) # Output: <class 'str'>Observa cómo True de Python se convirtió en true de JSON en la salida. La función dumps() gestiona automáticamente estas conversiones.
Para una salida más legible, usa el parámetro indent:
import json
student = {
"name": "Alice Johnson",
"age": 30,
"scores": [85, 92, 78, 95]
}
# Pretty-print con indentación
json_string = json.dumps(student, indent=2)
print(json_string)
# Output:
# {
# "name": "Alice Johnson",
# "age": 30,
# "scores": [
# 85,
# 92,
# 78,
# 95
# ]
# }El parámetro indent especifica cuántos espacios usar para cada nivel de indentación. Esto hace que JSON sea mucho más fácil de leer, especialmente para estructuras anidadas complejas.
39.3.3) Convertir JSON a datos de Python con loads()
La función loads() (load string) convierte cadenas con formato JSON de vuelta a estructuras de datos de Python:
import json
# Cadena JSON (como podrías recibir de una API web)
json_string = '{"name": "Bob Smith", "age": 25, "scores": [90, 88, 92]}'
# Convertir a diccionario de Python
student = json.loads(json_string)
print(student) # Output: {'name': 'Bob Smith', 'age': 25, 'scores': [90, 88, 92]}
print(type(student)) # Output: <class 'dict'>
# Acceder a los datos como cualquier diccionario de Python
print(f"Name: {student['name']}") # Output: Name: Bob Smith
print(f"Average score: {sum(student['scores']) / len(student['scores'])}")
# Output: Average score: 90.0true, false y null de JSON se convierten automáticamente a True, False y None de Python:
import json
json_string = '{"active": true, "verified": false, "middle_name": null}'
data = json.loads(json_string)
print(data) # Output: {'active': True, 'verified': False, 'middle_name': None}
print(type(data["active"])) # Output: <class 'bool'>
print(type(data["middle_name"])) # Output: <class 'NoneType'>39.3.4) Escribir JSON en archivos con dump()
La función dump() escribe datos de Python directamente en un archivo en formato JSON:
import json
# Registros de estudiantes
students = [
{"name": "Alice", "age": 20, "gpa": 3.8},
{"name": "Bob", "age": 22, "gpa": 3.5},
{"name": "Charlie", "age": 21, "gpa": 3.9}
]
# Escribir en un archivo JSON
with open("students.json", "w") as file:
json.dump(students, file, indent=2)
print("Data written to students.json")
# Output: Data written to students.jsonDespués de ejecutar este código, el archivo students.json contiene:
[
{
"name": "Alice",
"age": 20,
"gpa": 3.8
},
{
"name": "Bob",
"age": 22,
"gpa": 3.5
},
{
"name": "Charlie",
"age": 21,
"gpa": 3.9
}
]¿Por qué usar dump() en lugar de dumps()? La función dump() escribe directamente en un archivo, lo cual es más eficiente que convertir primero a una cadena y luego escribir esa cadena. Usa dump() para archivos y dumps() cuando necesites el JSON como cadena (por ejemplo, para enviarlo por una red).
39.3.5) Leer JSON desde archivos con load()
La función load() lee datos JSON desde un archivo y los convierte a estructuras de datos de Python:
import json
# Leer desde el archivo JSON que creamos antes
with open("students.json", "r") as file:
students = json.load(file)
print(f"Loaded {len(students)} students") # Output: Loaded 3 students
# Trabajar con los datos
for student in students:
print(f"{student['name']}: GPA {student['gpa']}")
# Output:
# Alice: GPA 3.8
# Bob: GPA 3.5
# Charlie: GPA 3.939.3.6) Manejar errores de JSON
Al trabajar con JSON, podrías encontrarte con datos inválidos. Maneja siempre posibles errores:
import json
# JSON inválido: falta la comilla de cierre
invalid_json = '{"name": "Alice", "age": 30'
try:
data = json.loads(invalid_json)
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e}")
# Output: Invalid JSON: Expecting ',' delimiter: line 1 column 28 (char 27)Esto es especialmente importante al leer JSON desde fuentes externas (archivos, APIs web, entrada del usuario) donde no puedes garantizar que los datos sean válidos:
import json
def load_config(filename):
"""Cargar la configuración desde un archivo JSON con manejo de errores"""
try:
with open(filename, "r") as file:
config = json.load(file)
return config
except FileNotFoundError:
print(f"Config file '{filename}' not found")
return None
except json.JSONDecodeError as e:
print(f"Invalid JSON in '{filename}': {e}")
return None
# Intentar cargar la configuración
config = load_config("config.json")
if config:
print(f"Configuration loaded: {config}")
else:
print("Using default configuration")39.3.7) Ejemplo práctico de JSON: guardar y cargar el estado de la aplicación
Aquí tienes un ejemplo completo que muestra cómo guardar y cargar datos de una aplicación:
import json
def save_game_state(filename, player_data):
"""Guardar el estado del juego en un archivo JSON"""
with open(filename, "w") as file:
json.dump(player_data, file, indent=2)
print(f"Game saved to {filename}")
def load_game_state(filename):
"""Cargar el estado del juego desde un archivo JSON"""
try:
with open(filename, "r") as file:
player_data = json.load(file)
print(f"Game loaded from {filename}")
return player_data
except FileNotFoundError:
print("No saved game found")
return None
# Datos del juego
player = {
"name": "Hero",
"level": 5,
"health": 85,
"inventory": ["sword", "shield", "potion"],
"position": {"x": 10, "y": 20}
}
# Guardar el juego
save_game_state("savegame.json", player)
# Output: Game saved to savegame.json
# Más tarde, cargar el juego
loaded_player = load_game_state("savegame.json")
# Output: Game loaded from savegame.json
if loaded_player:
print(f"Welcome back, {loaded_player['name']}!")
print(f"Level: {loaded_player['level']}, Health: {loaded_player['health']}")
# Output:
# Welcome back, Hero!
# Level: 5, Health: 8539.4) Contenedores prácticos en collections
El módulo collections proporciona tipos de contenedores especializados que amplían los contenedores integrados de Python (listas(list), diccionarios(dict), conjuntos(set)) con funcionalidad adicional. Estos contenedores resuelven problemas comunes de forma más elegante que usando estructuras de datos básicas.
39.4.1) Contar elementos con Counter
La clase Counter está diseñada para contar objetos hashables. Es una subclase de diccionario(dict) que almacena elementos como claves y sus conteos como valores.
Qué acepta Counter como entrada:
- Cualquier iterable (lista(list), cadena(string), tupla(tuple), etc.)
- Otro diccionario(dict) con conteos
- Argumentos con nombre (keyword arguments) con conteos
Qué almacena Counter:
- Un diccionario(dict) donde las claves son los elementos y los valores son sus conteos
- Ejemplo:
Counter(['a', 'b', 'a'])almacena{'a': 2, 'b': 1}
Ventaja clave frente a diccionarios(dict) normales:
- Devuelve 0 para claves faltantes en lugar de lanzar
KeyError - Proporciona métodos específicos de conteo como
most_common() - Admite operaciones aritméticas entre contadores
Uso básico
from collections import Counter
# Contar letras en una palabra
word = "mississippi"
letter_counts = Counter(word)
print(letter_counts)
# Output: Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
# Acceder a los conteos como un diccionario
print(f"Number of 'i's: {letter_counts['i']}")
# Output: Number of 'i's: 4
print(f"Number of 'z's: {letter_counts['z']}")
# Output: Number of 'z's: 0 (returns 0 for missing keys, no KeyError!)Crear Counters a partir de distintas fuentes
from collections import Counter
# Desde una lista
votes = ["Alice", "Bob", "Alice", "Charlie", "Alice", "Bob", "Alice"]
vote_counts = Counter(votes)
print(vote_counts)
# Output: Counter({'Alice': 4, 'Bob': 2, 'Charlie': 1})
# Desde una cadena (cuenta cada carácter)
letter_counts = Counter("hello")
print(letter_counts)
# Output: Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
# Desde un diccionario
existing_counts = {'apple': 3, 'banana': 2}
fruit_counts = Counter(existing_counts)
print(fruit_counts)
# Output: Counter({'apple': 3, 'banana': 2})
# Desde keyword arguments
color_counts = Counter(red=5, blue=3, green=2)
print(color_counts)
# Output: Counter({'red': 5, 'blue': 3, 'green': 2})Encontrar los elementos más comunes con most_common()
Firma del método: most_common(n=None)
Parámetros:
n(opcional): Número de elementos más comunes a devolver- Si
nse omite o esNone, devuelve todos los elementos
Devuelve:
- Una lista(list) de tuplas(tuple)
(item, count) - Ordenada por conteo, de mayor a menor
- Si los conteos son iguales, los elementos están en el orden en que se encontraron por primera vez
from collections import Counter
# Analizar la frecuencia de palabras en el texto
text = "the quick brown fox jumps over the lazy dog the fox"
words = text.split()
word_counts = Counter(words)
# Obtener las 3 palabras más comunes
top_3 = word_counts.most_common(3)
print(top_3)
# Output: [('the', 3), ('fox', 2), ('quick', 1)]Operaciones aritméticas con Counters
Puedes sumar, restar y realizar otras operaciones sobre objetos Counter:
from collections import Counter
# Contar elementos en dos grupos
group1 = Counter(["apple", "banana", "apple", "orange"])
print(group1)
# Output: Counter({'apple': 2, 'banana': 1, 'orange': 1})
group2 = Counter(["banana", "banana", "grape", "apple"])
print(group2)
# Output: Counter({'banana': 2, 'grape': 1, 'apple': 1})
# Sumar los conteos
combined = group1 + group2
print(combined)
# Output: Counter({'apple': 3, 'banana': 3, 'orange': 1, 'grape': 1})
# Restar conteos (solo conserva resultados positivos)
difference = group1 - group2
print(difference)
# Output: Counter({'apple': 1, 'orange': 1})
# banana: 1 - 2 = -1 (negative, so excluded)
# grape: not in group1, so excludedEjemplo práctico: analizar calificaciones de estudiantes
from collections import Counter
# Distribución de calificaciones
grades = ["A", "B", "A", "C", "B", "A", "B", "D", "A", "B", "C", "A"]
grade_counts = Counter(grades)
print(f"Total students: {len(grades)}")
# Output: Total students: 12
print("\nGrade Distribution:")
for grade, count in grade_counts.most_common():
percentage = (count / len(grades)) * 100
bar = "█" * count
print(f" {grade}: {count} students ({percentage:4.1f}%) {bar}")
# Output:
# Grade Distribution:
# A: 5 students (41.7%) █████
# B: 4 students (33.3%) ████
# C: 2 students (16.7%) ██
# D: 1 students ( 8.3%) █39.4.2) Diccionarios con valores predeterminados usando defaultdict
La clase defaultdict es una subclase de diccionario(dict) que crea automáticamente entradas con un valor por defecto cuando accedes a una clave inexistente. Esto elimina la necesidad de comprobar si las claves existen antes de usarlas.
Qué acepta defaultdict como entrada:
- Una función de fábrica predeterminada (default factory) (obligatorio): Un callable que devuelve el valor predeterminado para claves faltantes
- Cualquier argumento que un
dictnormal acepte (pares clave-valor, otro diccionario, keyword arguments)
Ventaja clave frente a diccionarios(dict) normales:
- No hace falta comprobar si una clave existe antes de usarla
- Inicializa automáticamente las claves faltantes con un valor predeterminado
- Código más limpio y legible para operaciones de agrupación, conteo y acumulación
Comprender la fábrica predeterminada
Cuando creas un defaultdict, debes proporcionar una fábrica predeterminada (default factory): un callable (función) que no recibe argumentos y devuelve el valor por defecto. Fábricas predeterminadas comunes:
int- devuelve0(útil para contar)list- devuelve[](útil para agrupar elementos)set- devuelveset()(útil para recopilar elementos únicos)str- devuelve''(útil para concatenación de cadenas)lambda: value- devuelve un valor predeterminado personalizado
from collections import defaultdict
# Distintas fábricas predeterminadas
counts = defaultdict(int) # Las claves faltantes devuelven 0
groups = defaultdict(list) # Las claves faltantes devuelven []
unique = defaultdict(set) # Las claves faltantes devuelven set()
custom = defaultdict(lambda: "N/A") # Las claves faltantes devuelven "N/A"
# Probar con claves faltantes
print(counts['missing']) # Output: 0
print(groups['missing']) # Output: []
print(unique['missing']) # Output: set()
print(custom['missing']) # Output: N/AUso básico: conteo con defaultdict
Compara un diccionario normal vs defaultdict para contar:
from collections import defaultdict
word = "mississippi"
# Diccionario normal: hay que comprobar si la clave existe
regular_dict = {}
for letter in word:
if letter not in regular_dict:
regular_dict[letter] = 0
regular_dict[letter] += 1
print(regular_dict)
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}
# defaultdict: crea automáticamente entradas con valor por defecto
letter_counts = defaultdict(int) # int() devuelve 0
for letter in word:
letter_counts[letter] += 1 # ¡No hace falta comprobar si la clave existe!
print(dict(letter_counts))
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}Cómo funciona:
- Cuando accedes a
letter_counts[letter]para una letra nueva,defaultdictllama aint()que devuelve0 - Se crea la clave con valor
0, luego+= 1lo convierte en1 - Para claves existentes, se comporta como un diccionario normal
Agrupar elementos con defaultdict(list)
Un caso de uso común es agrupar elementos en categorías:
from collections import defaultdict
students = [
("Alice", "A"),
("Bob", "B"),
("Charlie", "A"),
("Diana", "C"),
("Eve", "B"),
("Frank", "A")
]
# Agrupar estudiantes por calificación
# Con defaultdict: limpio y sencillo
students_by_grade = defaultdict(list)
for name, grade in students:
students_by_grade[grade].append(name)
print(dict(students_by_grade))
# Output: {'A': ['Alice', 'Charlie', 'Frank'], 'B': ['Bob', 'Eve'], 'C': ['Diana']}
# Acceder a una calificación que aún no existe
print(students_by_grade["D"]) # Output: [] (empty list, not KeyError!)Cómo funciona:
- Cuando accedes a
students_by_grade[grade]para una calificación nueva,defaultdictllama alist()que devuelve[] - Se crea la clave con una lista vacía, luego
.append(name)añade el primer estudiante - Para calificaciones existentes, simplemente añade a la lista existente
Crear defaultdict a partir de un diccionario existente
Puedes inicializar un defaultdict con datos existentes:
from collections import defaultdict
# Empezar con conteos existentes
existing_data = {'apple': 5, 'banana': 3}
# Crear defaultdict desde un diccionario existente
fruit_counts = defaultdict(int, existing_data)
# Añadir más conteos
fruit_counts['apple'] += 2 # 5 + 2 = 7
fruit_counts['orange'] += 1 # 0 + 1 = 1 (new key, starts at 0)
print(dict(fruit_counts))
# Output: {'apple': 7, 'banana': 3, 'orange': 1}Fábrica predeterminada personalizada
Puedes proporcionar cualquier callable como fábrica predeterminada:
from collections import defaultdict
# Usar lambda para valores predeterminados personalizados
page_views = defaultdict(lambda: {'views': 0, 'unique': 0})
page_views['home']['views'] = 100
page_views['home']['unique'] = 75
print(page_views['home'])
# Output: {'views': 100, 'unique': 75}
print(page_views['about']) # New key gets default dictionary
# Output: {'views': 0, 'unique': 0}Notas importantes
Acceder vs. comprobar claves:
from collections import defaultdict
counts = defaultdict(int)
# Acceder a una clave faltante la CREA
value = counts['missing'] # Creates 'missing' with value 0
print('missing' in counts) # Output: True
# Para comprobar sin crear, usa 'in' o .get()
counts2 = defaultdict(int)
print('missing' in counts2) # Output: False (doesn't create key)
print(counts2.get('missing')) # Output: None (doesn't create key)39.5) (Opcional) Herramientas útiles de iteración
El módulo itertools proporciona funciones para crear iteradores eficientes. Estas herramientas te ayudan a trabajar con secuencias de formas potentes sin crear grandes listas(list) intermedias.
39.5.1) Encadenar iterables con chain()
La función chain() combina varios iterables en un único iterador que produce elementos de cada iterable en secuencia.
Qué acepta chain():
- Varios iterables (listas(list), tuplas(tuple), cadenas(string), etc.) como argumentos separados
Qué devuelve chain():
- Un iterador que produce todos los elementos del primer iterable, luego todos los elementos del segundo, y así sucesivamente
Ventaja clave:
- Más eficiente en memoria que concatenar con
+(no crea listas intermedias) - Funciona con cualquier iterable, no solo con listas
from itertools import chain
# Combinar varias listas
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
combined = chain(list1, list2, list3)
print(list(combined)) # Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]Esto es más eficiente en memoria que concatenar listas con +, especialmente para secuencias grandes:
from itertools import chain
# Procesar varias fuentes de datos sin crear una gran lista combinada
students_class_a = ["Alice", "Bob", "Charlie"]
students_class_b = ["Diana", "Eve", "Frank"]
students_class_c = ["Grace", "Henry", "Iris"]
# Iterar sobre todos los estudiantes sin crear una lista combinada
for student in chain(students_class_a, students_class_b, students_class_c):
print(f"Processing: {student}")
# Output:
# Processing: Alice
# Processing: Bob
# Processing: Charlie
# Processing: Diana
# Processing: Eve
# Processing: Frank
# Processing: Grace
# Processing: Henry
# Processing: IrisPuedes encadenar distintos tipos de iterables:
from itertools import chain
# Encadenar listas, tuplas y cadenas
numbers = [1, 2, 3]
letters = ("a", "b", "c")
word = "xyz"
combined = chain(numbers, letters, word)
print(list(combined)) # Output: [1, 2, 3, 'a', 'b', 'c', 'x', 'y', 'z']39.5.2) Repetir elementos con cycle()
La función cycle() crea un iterador infinito que recorre repetidamente los elementos de un iterable.
Qué acepta cycle():
- Un único iterable (lista(list), tupla(tuple), cadena(string), etc.)
Qué devuelve cycle():
- Un iterador infinito que produce elementos del iterable repetidamente
- Después de llegar al final, empieza de nuevo desde el principio
Características clave:
- Crea un iterador infinito: nunca se detiene por sí solo
- Debe usarse con una condición de parada (contador,
breakozip()) - Eficiente en memoria: no crea copias de los datos
from itertools import cycle
# Crear un ciclo infinito de colores
colors = cycle(["red", "green", "blue"])
# Tomar los primeros 10 colores
for i, color in enumerate(colors):
if i >= 10:
break
print(f"Item {i}: {color}")
# Output:
# Item 0: red
# Item 1: green
# Item 2: blue
# Item 3: red
# Item 4: green
# Item 5: blue
# Item 6: red
# Item 7: green
# Item 8: blue
# Item 9: redAdvertencia: cycle() crea un iterador infinito. Úsalo siempre con una condición de parada (como un contador o una sentencia break), o crearás un bucle(loop) infinito.
Un caso de uso práctico es alternar entre valores:
from itertools import cycle
# Alternar entre dos colores de fondo para filas de una tabla
row_colors = cycle(["white", "lightgray"])
rows = ["Row 1", "Row 2", "Row 3", "Row 4", "Row 5"]
for row, color in zip(rows, row_colors):
print(f"{row}: background-color: {color}")
# Output:
# Row 1: background-color: white
# Row 2: background-color: lightgray
# Row 3: background-color: white
# Row 4: background-color: lightgray
# Row 5: background-color: whiteAquí usamos zip() (que aprendimos en el Capítulo 37) para emparejar cada fila con un color. El iterador cycle() repite automáticamente los colores según sea necesario.
39.5.3) Combinar chain() y cycle()
Puedes combinar funciones de itertools para patrones más complejos:
from itertools import chain, cycle
# Crear un patrón que cicla entre varias secuencias
pattern1 = [1, 2, 3]
pattern2 = [10, 20]
# Encadenar los patrones y luego ciclar el resultado
combined_pattern = cycle(chain(pattern1, pattern2))
# Tomar los primeros 12 valores
for i, value in enumerate(combined_pattern):
if i >= 12:
break
print(value, end=" ")
# Output: 1 2 3 10 20 1 2 3 10 20 1 2
print() # NewlineEsto crea un patrón repetitivo: 1, 2, 3, 10, 20, 1, 2, 3, 10, 20, ...
Aquí tienes un ejemplo práctico para crear un calendario rotativo:
from itertools import cycle
# Crear un calendario rotativo para miembros del equipo
team_members = ["Alice", "Bob", "Charlie"]
schedule = cycle(team_members)
# Asignar tareas a los miembros del equipo de forma rotativa
tasks = [
"Review code",
"Write tests",
"Update documentation",
"Fix bug #123",
"Implement feature X",
"Deploy to staging"
]
print("Task Assignments:")
for task, assignee in zip(tasks, schedule):
print(f" {assignee}: {task}")
# Output:
# Task Assignments:
# Alice: Review code
# Bob: Write tests
# Charlie: Update documentation
# Alice: Fix bug #123
# Bob: Implement feature X
# Charlie: Deploy to stagingEn este capítulo, exploramos cinco módulos esenciales de la biblioteca estándar que amplían las capacidades de Python:
random: Genera números aleatorios, haz selecciones aleatorias y baraja secuencias: esencial para simulaciones, juegos y pruebasdatetime: Trabaja con fechas, horas y duraciones: calcula edades, programa eventos y da formato a marcas de tiempojson: Intercambia datos con otros programas usando el formato universal JSON: guarda el estado de la aplicación, trabaja con APIs web y almacena configuracióncollections: Usa contenedores especializados comoCounterpara contar ydefaultdictpara crear claves automáticamenteitertools: Crea iteradores eficientes conchain()para combinar secuencias ycycle()para repetir patrones
Estos módulos forman parte de la biblioteca estándar de Python: siempre están disponibles, están bien probados y resuelven problemas comunes de programación de forma elegante. A medida que construyas programas más complejos, verás que recurres a estas herramientas con frecuencia. Representan la filosofía de Python de "batteries included": proporcionar soluciones potentes y listas para usar para tareas de programación cotidianas.