Python & AI Tutorials Logo
Programação Python

39. Módulos Essenciais da Biblioteca Padrão

A biblioteca padrão (standard library) do Python é uma coleção de módulos que já vêm embutidos no Python — você não precisa instalar nada extra para usá-los. Esses módulos fornecem ferramentas poderosas para tarefas comuns de programação: gerar números aleatórios, trabalhar com datas e horários, trocar dados com outros programas e usar estruturas de dados especializadas que vão além de listas e dicionários básicos.

Neste capítulo, vamos explorar cinco módulos essenciais da biblioteca padrão que você vai usar com frequência na programação Python do mundo real.

39.1) Gerando Aleatoriedade com random

O módulo random fornece funções para gerar números aleatórios e fazer seleções aleatórias. Isso é útil para simulações, jogos, testes, amostragem de dados e qualquer situação em que você precise de comportamento imprevisível.

39.1.1) Gerando Inteiros Aleatórios com randint()

A função randint() gera um inteiro aleatório entre dois valores, incluindo as duas extremidades:

python
import random
 
# Simular a rolagem de um dado de seis lados
die_roll = random.randint(1, 6)
print(f"You rolled: {die_roll}")  # Output: You rolled: 4 (varies each run)
 
# Gerar uma idade aleatória entre 18 e 65
age = random.randint(18, 65)
print(f"Random age: {age}")  # Output: Random age: 42 (varies)

Observe que tanto o valor inicial quanto o valor final são incluídos nos resultados possíveis. randint(1, 6) pode retornar 1, 2, 3, 4, 5 ou 6 — todos os seis valores são possíveis.

Aqui vai um exemplo prático que simula várias rolagens de dados:

python
import random
 
# Simular a rolagem de dois dados e calcular a soma deles
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 que as duas extremidades são incluídas: Isso torna randint() intuitiva para casos de uso comuns. Quando você quer um número de 1 a 6 (como um dado), você escreve randint(1, 6) e tanto 1 quanto 6 são resultados possíveis.

39.1.2) Gerando Números de Ponto Flutuante Aleatórios

Para números decimais aleatórios, use random() (retorna um float entre 0.0 e 1.0) ou uniform() (retorna um float entre dois valores especificados):

python
import random
 
# Gerar um float aleatório entre 0.0 e 1.0 (0.0 incluído, 1.0 excluído)
probability = random.random()
print(f"Random probability: {probability:.4f}")  # Output: Random probability: 0.7284 (varies)
 
# Gerar uma temperatura aleatória entre 15.0 e 30.0 graus
temperature = random.uniform(15.0, 30.0)
print(f"Temperature: {temperature:.2f}°C")  # Output: Temperature: 23.47°C (varies)
 
# Gerar um preço aleatório entre $10.00 e $99.99
price = random.uniform(10.0, 99.99)
print(f"Price: ${price:.2f}")  # Output: Price: $45.67 (varies)

A função random() é útil quando você precisa de um valor de probabilidade ou uma porcentagem. A função uniform() é melhor quando você precisa de um decimal aleatório dentro de um intervalo específico.

39.1.3) Fazendo Escolhas Aleatórias com choice()

A função choice() seleciona aleatoriamente um elemento de uma sequência (lista, tupla ou string):

python
import random
 
# Selecionar aleatoriamente uma cor
colors = ["red", "blue", "green", "yellow", "purple"]
selected_color = random.choice(colors)
print(f"Selected color: {selected_color}")  # Output: Selected color: green (varies)
 
# Selecionar aleatoriamente um vencedor entre participantes
participants = ["Alice", "Bob", "Charlie", "Diana"]
winner = random.choice(participants)
print(f"The winner is: {winner}")  # Output: The winner is: Bob (varies)
 
# Selecionar aleatoriamente um caractere de uma string
vowels = "aeiou"
random_vowel = random.choice(vowels)
print(f"Random vowel: {random_vowel}")  # Output: Random vowel: i (varies)

Isso é particularmente útil para jogos, amostragem aleatória ou seleção de dados de teste aleatórios. Cada elemento da sequência tem a mesma probabilidade de ser escolhido.

Aqui vai um exemplo mais complexo que simula um jogo simples de quiz:

python
import random
 
# Perguntas do quiz com suas respostas
questions = [
    ("What is 2 + 2?", "4"),
    ("What is the capital of France?", "Paris"),
    ("What color is the sky?", "blue")
]
 
# Selecionar aleatoriamente uma pergunta
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) Selecionando Vários Itens Aleatórios com sample()

Quando você precisa selecionar vários itens únicos de uma sequência, use sample(). Isso é como tirar cartas de um baralho sem reposição — depois que um item é selecionado, ele não será selecionado novamente:

python
import random
 
# Selecionar 3 alunos aleatórios para um trabalho em 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)
 
# Sortear 5 números de loteria de 1 a 50 (sem duplicatas)
lottery_numbers = random.sample(range(1, 51), 5)
lottery_numbers.sort()  # Ordenar para exibição
print(f"Lottery numbers: {lottery_numbers}")  # Output: Lottery numbers: [7, 15, 23, 38, 49] (varies)

O segundo argumento de sample() especifica quantos itens selecionar. O número deve ser menor ou igual ao tamanho da sequência — você não pode selecionar mais itens do que existem disponíveis.

39.1.5) Embaralhando Sequências com shuffle()

A função shuffle() reordena aleatoriamente os elementos de uma lista no lugar (modificando a lista original):

python
import random
 
# Embaralhar um baralho 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)
 
# Embaralhar perguntas do quiz para uma ordem aleatória
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)

Funções do Módulo Random

randint: Inteiros aleatórios

random/uniform: Floats aleatórios

choice: Escolher um item

sample: Escolher vários itens únicos

shuffle: Reordenar a lista no lugar

Inclui as duas extremidades

random: 0.0 a 1.0

uniform: Intervalo personalizado

Probabilidade igual para cada

Sem duplicatas

Modifica a lista original

39.2) Trabalhando com Datas e Horários

O módulo datetime fornece classes para trabalhar com datas, horários e intervalos de tempo. Isso é essencial para agendamento, logging, cálculo de durações e qualquer aplicação que precise acompanhar quando as coisas acontecem.

39.2.1) Obtendo a Data e Hora Atuais

A classe datetime representa um ponto específico no tempo com componentes de data e hora:

python
from datetime import datetime
 
# Obter a data e hora atuais
now = datetime.now()
print(f"Current datetime: {now}")
# Output: Current datetime: 2026-01-02 14:30:45.123456
 
# Acessar componentes individuais
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: 45

Para apenas a data (sem hora), use a classe date:

python
from datetime import date
 
# Obter a data de hoje
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: 2

39.2.2) Criando Datas e Horários Específicos

Você pode criar objetos datetime e date para pontos específicos no tempo:

python
from datetime import datetime, date
 
# Criar uma data específica
birthday = date(1995, 7, 15)
print(f"Birthday: {birthday}")  # Output: Birthday: 1995-07-15
 
# Criar um 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:00

Isso é útil para representar prazos, compromissos, datas históricas ou qualquer ponto fixo no tempo:

python
from datetime import date
 
# Datas importantes em um projeto
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-30

39.2.3) Calculando Diferenças de Tempo com timedelta

A classe timedelta representa uma duração — a diferença entre duas datas ou horários. Você pode usá-la para calcular quanto tempo passou ou para adicionar/subtrair tempo de datas:

python
from datetime import date, timedelta
 
# Calcular idade
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): 30

Quando você subtrai uma data de outra, você obtém um objeto timedelta. O atributo days informa o número de dias nessa duração.

Você também pode criar objetos timedelta diretamente para representar durações específicas:

python
from datetime import date, timedelta
 
# Adicionar dias a uma data
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
 
# Subtrair dias de uma data
thirty_days_ago = today - timedelta(days=30)
print(f"30 days ago: {thirty_days_ago}")  # Output: 30 days ago: 2025-12-03

timedelta pode representar dias, segundos, microssegundos, milissegundos, minutos, horas e semanas:

python
from datetime import datetime, timedelta
 
# Calcular um prazo
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 tempo restante
time_left = deadline - now
print(f"Hours remaining: {time_left.total_seconds() / 3600}")  # Output: Hours remaining: 48.5

O método total_seconds() converte toda a duração para segundos, que você pode então converter para horas, minutos ou qualquer outra unidade.

Aqui vai um exemplo prático calculando marcos de um projeto:

python
from datetime import date, timedelta
 
# Planejamento do projeto
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-26

39.2.4) Comparando Datas e Horários

Objetos date e datetime podem ser comparados usando operadores de comparação padrão:

python
from datetime import date
 
# Comparar datas
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: True

Isso é útil para verificar prazos, validar intervalos de datas e ordenar datas:

python
from datetime import date
 
# Verificar se uma data está no passado
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 uma lista de datas
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-15

39.2.5) Formatando Datas e Horários com strftime()

O método strftime() (string format time) converte datas e horários em strings formatadas. Você especifica o formato usando códigos especiais:

python
from datetime import datetime
 
now = datetime(2026, 1, 2, 14, 30, 45)
 
# Formatos comuns de data
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 comuns de hora
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 PM

Códigos de formato comuns:

CódigoDescriçãoExemplo
%YAno com século2026
%mMês como número com zero à esquerda (01-12)01
%dDia como número com zero à esquerda (01-31)02
%BNome completo do mêsJanuary
%bNome abreviado do mêsJan
%ANome completo do dia da semanaFriday
%aNome abreviado do dia da semanaFri
%HHora no formato 24h (00-23)14
%IHora no formato 12h (01-12)02
%MMinuto (00-59)30
%SSegundo (00-59)45
%pAM/PMPM

Aqui vai um exemplo prático criando uma entrada de log:

python
from datetime import datetime
 
def log_event(message):
    """Log an event with a timestamp"""
    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 successfully

39.2.6) Fazendo Parse de Datas a Partir de Strings com strptime()

A função strptime() (string parse time) converte strings formatadas de volta em objetos datetime. Você especifica os mesmos códigos de formato para dizer ao Python como interpretar a string:

python
from datetime import datetime
 
# Fazer parse de diferentes formatos de data
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
 
# Fazer parse de datetime com 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:00

Isso é essencial ao ler datas de arquivos, entrada do usuário ou fontes de dados externas:

python
from datetime import datetime
 
# Fazer parse da entrada do usuário
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: A string de formato deve corresponder exatamente à string de entrada, ou você vai receber um ValueError:

python
from datetime import datetime
 
# Isso vai falhar - o formato não corresponde
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'

Módulo datetime

datetime.now: Data/hora atual

date.today: Data atual

datetime/date: Criar datas específicas

timedelta: Durações de tempo

strftime: Formatar para string

strptime: Fazer parse de string

Adicionar/subtrair de datas

Calcular diferenças

%Y, %m, %d, %H, %M, %S

Deve corresponder exatamente ao formato

39.3) Lendo e Escrevendo Dados JSON

JSON (JavaScript Object Notation) é um formato de texto para armazenar e trocar dados estruturados. É o formato mais comum para APIs web, arquivos de configuração e troca de dados entre programas. O módulo json do Python facilita a conversão entre estruturas de dados do Python e texto JSON.

39.3.1) Entendendo a Estrutura do JSON

JSON se parece com dicionários e listas Python, mas com algumas diferenças:

JSON suporta estes tipos de dados:

  • Objetos (como dicionários Python): {"name": "Alice", "age": 30}
  • Arrays (como listas Python): [1, 2, 3, 4]
  • Strings: "hello" (deve usar aspas duplas)
  • Números: 42, 3.14
  • Booleanos: true, false (minúsculos)
  • Nulo: null (como None do Python)

Principais diferenças em relação ao Python:

  • JSON usa true/false/null em vez de True/False/None do Python
  • Strings em JSON devem usar aspas duplas ("text"), não aspas simples
  • JSON não oferece suporte direto a tuplas, sets ou objetos personalizados

Veja como são dados JSON:

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"
    }
}

Observação: Isso é texto JSON puro, não código Python. Repare no true em minúsculas e no uso de aspas duplas.

39.3.2) Convertendo Dados Python para JSON com dumps()

A função dumps() (dump string) converte estruturas de dados Python em strings formatadas em JSON:

python
import json
 
student = {
    "name": "Alice Johnson",
    "age": 30,
    "email": "alice@example.com",
    "is_active": True,
    "scores": [85, 92, 78, 95]
}
 
# Converter um dicionário para 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'>

Repare como o True do Python virou true do JSON na saída. A função dumps() lida automaticamente com essas conversões.

Para uma saída mais legível, use o parâmetro indent:

python
import json
 
student = {
    "name": "Alice Johnson",
    "age": 30,
    "scores": [85, 92, 78, 95]
}
 
# Imprimir de forma bonita com indentação
json_string = json.dumps(student, indent=2)
print(json_string)
# Output:
# {
#   "name": "Alice Johnson",
#   "age": 30,
#   "scores": [
#     85,
#     92,
#     78,
#     95
#   ]
# }

O parâmetro indent especifica quantos espaços usar para cada nível de indentação. Isso deixa o JSON bem mais fácil de ler, especialmente para estruturas complexas aninhadas.

39.3.3) Convertendo JSON para Dados Python com loads()

A função loads() (load string) converte strings formatadas em JSON de volta em estruturas de dados Python:

python
import json
 
# String JSON (como você poderia receber de uma API web)
json_string = '{"name": "Bob Smith", "age": 25, "scores": [90, 88, 92]}'
 
# Converter para dicionário Python
student = json.loads(json_string)
print(student)  # Output: {'name': 'Bob Smith', 'age': 25, 'scores': [90, 88, 92]}
print(type(student))  # Output: <class 'dict'>
 
# Acessar os dados como qualquer dicionário Python
print(f"Name: {student['name']}")  # Output: Name: Bob Smith
print(f"Average score: {sum(student['scores']) / len(student['scores'])}")
# Output: Average score: 90.0

true, false e null do JSON são convertidos automaticamente para True, False e None do Python:

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) Escrevendo JSON em Arquivos com dump()

A função dump() escreve dados Python diretamente em um arquivo no formato JSON:

python
import json
 
# Registros de alunos
students = [
    {"name": "Alice", "age": 20, "gpa": 3.8},
    {"name": "Bob", "age": 22, "gpa": 3.5},
    {"name": "Charlie", "age": 21, "gpa": 3.9}
]
 
# Escrever em um arquivo 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.json

Depois de executar esse código, o arquivo students.json contém:

json
[
  {
    "name": "Alice",
    "age": 20,
    "gpa": 3.8
  },
  {
    "name": "Bob",
    "age": 22,
    "gpa": 3.5
  },
  {
    "name": "Charlie",
    "age": 21,
    "gpa": 3.9
  }
]

Por que usar dump() em vez de dumps()? A função dump() escreve diretamente em um arquivo, o que é mais eficiente do que converter para uma string primeiro e depois escrever a string. Use dump() para arquivos e dumps() quando você precisar do JSON como uma string (por exemplo, para enviar pela rede).

39.3.5) Lendo JSON de Arquivos com load()

A função load() lê dados JSON de um arquivo e os converte para estruturas de dados Python:

python
import json
 
# Ler do arquivo JSON que criamos anteriormente
with open("students.json", "r") as file:
    students = json.load(file)
 
print(f"Loaded {len(students)} students")  # Output: Loaded 3 students
 
# Trabalhar com os dados
for student in students:
    print(f"{student['name']}: GPA {student['gpa']}")
# Output:
# Alice: GPA 3.8
# Bob: GPA 3.5
# Charlie: GPA 3.9

39.3.6) Lidando com Erros de JSON

Ao trabalhar com JSON, você pode encontrar dados inválidos. Sempre trate erros potenciais:

python
import json
 
# JSON inválido - faltando aspas de fechamento
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)

Isso é especialmente importante ao ler JSON de fontes externas (arquivos, APIs web, entrada do usuário) em que você não pode garantir que os dados sejam válidos:

python
import json
 
def load_config(filename):
    """Carregar configuração de um arquivo JSON com tratamento de erros"""
    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
 
# Tentar carregar a configuração
config = load_config("config.json")
if config:
    print(f"Configuration loaded: {config}")
else:
    print("Using default configuration")

39.3.7) Exemplo Prático de JSON: Salvando e Carregando o Estado da Aplicação

Aqui vai um exemplo completo mostrando como salvar e carregar dados da aplicação:

python
import json
 
def save_game_state(filename, player_data):
    """Salvar o estado do jogo em um arquivo 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):
    """Carregar o estado do jogo de um arquivo 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
 
# Dados do jogo
player = {
    "name": "Hero",
    "level": 5,
    "health": 85,
    "inventory": ["sword", "shield", "potion"],
    "position": {"x": 10, "y": 20}
}
 
# Salvar o jogo
save_game_state("savegame.json", player)
# Output: Game saved to savegame.json
 
# Mais tarde, carregar o jogo
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: 85

Módulo json

dumps: Python → string JSON

loads: string JSON → Python

dump: Python → arquivo JSON

load: arquivo JSON → Python

Parâmetro indent para legibilidade

Lida com conversões de tipo

Mais eficiente que dumps + write

Tratar JSONDecodeError

39.4) Containers Práticos em collections

O módulo collections fornece tipos de container especializados que estendem os containers embutidos do Python (listas, dicionários, sets) com funcionalidade extra. Esses containers resolvem problemas comuns de forma mais elegante do que usar estruturas de dados básicas.

39.4.1) Contando Itens com Counter

A classe Counter foi projetada para contar objetos hasheáveis. Ela é uma subclasse de dicionário que armazena itens como chaves e suas contagens como valores.

O que Counter aceita como entrada:

  • Qualquer iterável (lista, string, tupla, etc.)
  • Outro dicionário com contagens
  • Argumentos nomeados (keyword arguments) com contagens

O que Counter armazena:

  • Um dicionário em que as chaves são os itens e os valores são suas contagens
  • Exemplo: Counter(['a', 'b', 'a']) armazena {'a': 2, 'b': 1}

Vantagem principal sobre dicionários comuns:

  • Retorna 0 para chaves ausentes em vez de levantar KeyError
  • Fornece métodos específicos para contagem, como most_common()
  • Suporta operações aritméticas entre counters

Uso Básico

python
from collections import Counter
 
# Contar letras em uma palavra
word = "mississippi"
letter_counts = Counter(word)
print(letter_counts)
# Output: Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
 
# Acessar contagens como um dicionário
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!)

Criando Counters a Partir de Diferentes Fontes

python
from collections import Counter
 
# A partir de uma lista
votes = ["Alice", "Bob", "Alice", "Charlie", "Alice", "Bob", "Alice"]
vote_counts = Counter(votes)
print(vote_counts)
# Output: Counter({'Alice': 4, 'Bob': 2, 'Charlie': 1})
 
# A partir de uma string (conta cada caractere)
letter_counts = Counter("hello")
print(letter_counts)
# Output: Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
 
# A partir de um dicionário
existing_counts = {'apple': 3, 'banana': 2}
fruit_counts = Counter(existing_counts)
print(fruit_counts)
# Output: Counter({'apple': 3, 'banana': 2})
 
# A partir de argumentos nomeados
color_counts = Counter(red=5, blue=3, green=2)
print(color_counts)
# Output: Counter({'red': 5, 'blue': 3, 'green': 2})

Encontrando Itens Mais Comuns com most_common()

Assinatura do método: most_common(n=None)

Parâmetros:

  • n (opcional): Número de itens mais comuns a retornar
  • Se n for omitido ou None, retorna todos os itens

Retorna:

  • Uma lista de tuplas (item, count)
  • Ordenada pela contagem, do maior para o menor
  • Se as contagens forem iguais, os itens ficam na ordem em que foram encontrados primeiro
python
from collections import Counter
 
# Analisar frequência de palavras no texto
text = "the quick brown fox jumps over the lazy dog the fox"
words = text.split()
word_counts = Counter(words)
 
# Obter as 3 palavras mais comuns
top_3 = word_counts.most_common(3)
print(top_3)
# Output: [('the', 3), ('fox', 2), ('quick', 1)]

Operações Aritméticas em Counters

Você pode somar, subtrair e realizar outras operações em objetos Counter:

python
from collections import Counter
 
# Contar itens em dois 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})
 
# Somar as contagens
combined = group1 + group2
print(combined)
# Output: Counter({'apple': 3, 'banana': 3, 'orange': 1, 'grape': 1})
 
# Subtrair as contagens (mantém apenas 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 excluded

Exemplo Prático: Analisando Notas de Alunos

python
from collections import Counter
 
# Distribuição de notas
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) Dicionários com Valores Padrão Usando defaultdict

A classe defaultdict é uma subclasse de dicionário que cria automaticamente entradas com um valor padrão quando você acessa uma chave ausente. Isso elimina a necessidade de verificar se as chaves existem antes de usá-las.

O que defaultdict aceita como entrada:

  • Uma função default factory (obrigatória): Um callable que retorna o valor padrão para chaves ausentes
  • Quaisquer argumentos que um dict normal aceita (pares chave-valor, outro dicionário, argumentos nomeados)

Vantagem principal sobre dicionários comuns:

  • Não precisa verificar se uma chave existe antes de usá-la
  • Inicializa automaticamente chaves ausentes com um valor padrão
  • Código mais limpo e legível para operações de agrupamento, contagem e acumulação

Entendendo a Default Factory

Quando você cria um defaultdict, você deve fornecer uma default factory — um callable (função) que não recebe argumentos e retorna o valor padrão. Default factories comuns:

  • int - retorna 0 (útil para contagem)
  • list - retorna [] (útil para agrupar itens)
  • set - retorna set() (útil para coletar itens únicos)
  • str - retorna '' (útil para concatenação de strings)
  • lambda: value - retorna um valor padrão personalizado
python
from collections import defaultdict
 
# Diferentes default factories
counts = defaultdict(int)        # Chaves ausentes retornam 0
groups = defaultdict(list)       # Chaves ausentes retornam []
unique = defaultdict(set)        # Chaves ausentes retornam set()
custom = defaultdict(lambda: "N/A")  # Chaves ausentes retornam "N/A"
 
# Testar com chaves ausentes
print(counts['missing'])     # Output: 0
print(groups['missing'])     # Output: []
print(unique['missing'])     # Output: set()
print(custom['missing'])     # Output: N/A

Uso Básico: Contando com defaultdict

Compare dicionário normal vs defaultdict para contagem:

python
from collections import defaultdict
 
word = "mississippi"
 
# Dicionário normal - precisa verificar se a chave 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 - cria automaticamente entradas com valor padrão
letter_counts = defaultdict(int)  # int() retorna 0
for letter in word:
    letter_counts[letter] += 1  # Não precisa verificar se a chave existe!
 
print(dict(letter_counts))
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}

Como funciona:

  1. Quando você acessa letter_counts[letter] para uma letra nova, o defaultdict chama int() que retorna 0
  2. A chave é criada com valor 0, então += 1 faz virar 1
  3. Para chaves existentes, ele se comporta como um dicionário normal

Agrupando Itens com defaultdict(list)

Um caso de uso comum é agrupar itens em categorias:

python
from collections import defaultdict
 
students = [
    ("Alice", "A"),
    ("Bob", "B"),
    ("Charlie", "A"),
    ("Diana", "C"),
    ("Eve", "B"),
    ("Frank", "A")
]
 
# Agrupar alunos por nota
# Com defaultdict - limpo e simples
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']}
 
# Acessar uma nota que ainda não existe
print(students_by_grade["D"])  # Output: [] (empty list, not KeyError!)

Como funciona:

  1. Quando você acessa students_by_grade[grade] para uma nota nova, o defaultdict chama list() que retorna []
  2. A chave é criada com uma lista vazia, então .append(name) adiciona o primeiro aluno
  3. Para notas existentes, ele apenas adiciona na lista já existente

Criando defaultdict a Partir de um Dicionário Existente

Você pode inicializar um defaultdict com dados existentes:

python
from collections import defaultdict
 
# Começar com contagens existentes
existing_data = {'apple': 5, 'banana': 3}
 
# Criar defaultdict a partir de um dicionário existente
fruit_counts = defaultdict(int, existing_data)
 
# Adicionar mais contagens
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}

Default Factory Personalizada

Você pode fornecer qualquer callable como default factory:

python
from collections import defaultdict
 
# Usar lambda para valores padrão 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}

Observações Importantes

Acessar vs. Checar Chaves:

python
from collections import defaultdict
 
counts = defaultdict(int)
 
# Acessar uma chave ausente a CRIA
value = counts['missing']  # Cria 'missing' com valor 0
print('missing' in counts)  # Output: True
 
# Para checar sem criar, use 'in' ou .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) Ferramentas Úteis de Iteração

O módulo itertools fornece funções para criar iteradores eficientes. Essas ferramentas ajudam você a trabalhar com sequências de formas poderosas sem criar grandes listas intermediárias.

39.5.1) Encadeando Iteráveis com chain()

A função chain() combina vários iteráveis em um único iterador que produz elementos de cada iterável em sequência.

O que chain() aceita:

  • Vários iteráveis (listas, tuplas, strings etc.) como argumentos separados

O que chain() retorna:

  • Um iterador que produz todos os elementos do primeiro iterável, depois todos os elementos do segundo, e assim por diante

Vantagem principal:

  • Mais eficiente em memória do que concatenar com + (não cria listas intermediárias)
  • Funciona com qualquer iterável, não apenas listas
python
from itertools import chain
 
# Combinar várias 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]

Isso é mais eficiente em memória do que concatenar listas com +, especialmente para sequências grandes:

python
from itertools import chain
 
# Processar várias fontes de dados sem criar uma grande lista combinada
students_class_a = ["Alice", "Bob", "Charlie"]
students_class_b = ["Diana", "Eve", "Frank"]
students_class_c = ["Grace", "Henry", "Iris"]
 
# Iterar sobre todos os alunos sem criar uma 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: Iris

Você pode encadear diferentes tipos de iteráveis:

python
from itertools import chain
 
# Encadear listas, tuplas e strings
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) Repetindo Elementos com cycle()

A função cycle() cria um iterador infinito que cicla repetidamente pelos elementos de um iterável.

O que cycle() aceita:

  • Um único iterável (lista, tupla, string etc.)

O que cycle() retorna:

  • Um iterador infinito que produz elementos do iterável repetidamente
  • Depois de chegar ao fim, recomeça do início

Principais características:

  • Cria um iterador infinito — nunca para por conta própria
  • Deve ser usado com uma condição de parada (contador, break ou zip())
  • Eficiente em memória: não cria cópias dos dados
python
from itertools import cycle
 
# Criar um ciclo infinito de cores
colors = cycle(["red", "green", "blue"])
 
# Pegar as primeiras 10 cores
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: red

Aviso: cycle() cria um iterador infinito. Sempre use com uma condição de parada (como um contador ou uma instrução break), senão você vai criar um loop infinito.

Um caso de uso prático é alternar entre valores:

python
from itertools import cycle
 
# Alternar entre duas cores de fundo para linhas de tabela
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: white

Aqui usamos zip() (que aprendemos no Capítulo 37) para parear cada linha com uma cor. O iterador cycle() repete automaticamente as cores conforme necessário.

39.5.3) Combinando chain() e cycle()

Você pode combinar funções de itertools para padrões mais complexos:

python
from itertools import chain, cycle
 
# Criar um padrão que cicla por várias sequências
pattern1 = [1, 2, 3]
pattern2 = [10, 20]
 
# Encadear os padrões e então ciclar o resultado
combined_pattern = cycle(chain(pattern1, pattern2))
 
# Pegar os primeiros 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()  # Output: Newline

Isso cria um padrão repetido: 1, 2, 3, 10, 20, 1, 2, 3, 10, 20, ...

Aqui vai um exemplo prático criando uma escala rotativa:

python
from itertools import cycle
 
# Criar uma escala rotativa para membros do time
team_members = ["Alice", "Bob", "Charlie"]
schedule = cycle(team_members)
 
# Atribuir tarefas aos membros do time em rotação
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 staging

Módulo itertools

chain: Combinar iteráveis

cycle: Repetir para sempre

Mais eficiente em memória do que +

Funciona com tipos diferentes

Cria um iterador infinito

Sempre use com condição de parada

Útil para padrões alternados


Neste capítulo, exploramos cinco módulos essenciais da biblioteca padrão que estendem as capacidades do Python:

  • random: Gerar números aleatórios, fazer seleções aleatórias e embaralhar sequências — essencial para simulações, jogos e testes
  • datetime: Trabalhar com datas, horários e durações — calcular idades, agendar eventos e formatar timestamps
  • json: Trocar dados com outros programas usando o formato JSON universal — salvar estado da aplicação, trabalhar com APIs web e armazenar configuração
  • collections: Usar containers especializados como Counter para contagem e defaultdict para criação automática de chaves
  • itertools: Criar iteradores eficientes com chain() para combinar sequências e cycle() para repetir padrões

Esses módulos fazem parte da biblioteca padrão do Python — eles estão sempre disponíveis, são bem testados e resolvem problemas comuns de programação de forma elegante. À medida que você construir programas mais complexos, vai perceber que recorre a essas ferramentas com frequência. Eles representam a filosofia do Python de “baterias incluídas” (batteries included) — fornecendo soluções poderosas e prontas para uso para tarefas do dia a dia em programação.


© 2025. Primesoft Co., Ltd.
support@primesoft.ai