Skip to content
This page has been auto-translated and may contain errors.View in English

Módulos e a biblioteca padrão

Python vem com uma enorme coleção de ferramentas prontas para uso: aleatoriedade, matemática, datas, caminhos de arquivos e muito mais. Essas ferramentas vivem em módulos, e você as traz para o seu código com import. Você já usou import json no capítulo anterior. Este capítulo cobre imports completamente e apresenta as partes mais úteis da biblioteca padrão.

A biblioteca padrão do Python fornece soluções testadas e documentadas para problemas comuns. Módulos são a unidade de organização de código: cada arquivo é um módulo, cada diretório com um __init__.py é um pacote. O sistema de import encontra módulos, compila-os se necessário e os armazena em cache em sys.modules para que sejam carregados apenas uma vez.

O sistema de import é uma maquinaria em camadas: finders localizam módulos e loaders os compilam e executam. Os resultados são armazenados em cache em sys.modules. import foo executa foo.py uma vez e vincula o objeto do módulo a foo no namespace atual. from foo import bar vincula apenas bar. Entender sys.path, __init__.py e imports relativos é essencial para construir pacotes.

Importando módulos

O import mais simples traz um módulo inteiro e permite que você use seu conteúdo com notação de ponto. Você também pode importar nomes específicos de um módulo para usá-los diretamente sem o prefixo. Apelidos encurtam nomes longos.

import module vincula o objeto do módulo ao nome module no escopo atual. from module import name vincula apenas name. Apelidos (import module as alias) são comuns com bibliotecas de terceiros. Evite from module import *: isso polui o namespace e torna pouco claro de onde os nomes vieram.

import module dispara toda a maquinaria de import, armazena em cache o resultado em sys.modules e vincula o objeto do módulo. from module import name é açúcar sintático: ainda importa o módulo inteiro e depois extrai name. Imports circulares são uma armadilha comum; a solução geralmente é mover imports para dentro de funções ou reestruturar as dependências entre módulos. importlib.import_module() permite imports programáticos.

python
import math

math.sqrt(16)     # 4.0
math.pi           # 3.141592653589793
math.floor(3.9)   # 3
math.ceil(3.1)    # 4

Importe nomes específicos de um módulo para que você possa usá-los diretamente:

python
from math import sqrt, pi

sqrt(16)    # 4.0 (sem necessidade do prefixo "math.")
pi          # 3.141592653589793

Dê a um módulo ou nome um apelido para encurtá-lo:

python
import math as m

m.sqrt(16)    # 4.0

from math import sqrt as square_root
square_root(25)    # 5.0

Apelidos são comuns com bibliotecas populares de terceiros (import numpy as np, import pandas as pd). Para módulos da biblioteca padrão, prefira usar o nome completo; isso torna o código mais fácil de ler.

random

O módulo random gera números aleatórios e faz escolhas aleatórias. Use-o para jogos, simulações, amostragem aleatória e qualquer outra coisa que precise de imprevisibilidade. Definir uma seed torna os resultados reproduzíveis: a mesma seed produz a mesma sequência toda vez.

random usa um gerador de números pseudo-aleatórios Mersenne Twister. A seed determina toda a sequência; a mesma seed sempre produz a mesma saída. .choice() escolhe um item, .choices() escolhe com reposição, .sample() escolhe sem reposição. .shuffle() modifica a lista no lugar e retorna None.

random usa um PRNG Mersenne Twister (MT19937) com estado de 624 palavras. random.seed() inicializa o estado; sem isso, o estado é semeado a partir de os.urandom(). Para fins criptográficos, use secrets: random não é criptograficamente seguro. random.SystemRandom() envolve os.urandom() para uma alternativa segura com a mesma API.

python
import random

random.random()              # float entre 0 e 1 (exclusivo)
random.randint(1, 10)        # inteiro de 1 a 10 (ambos inclusivos)
random.uniform(1.0, 10.0)    # float entre 1.0 e 10.0

colours = ["red", "green", "blue"]
random.choice(colours)       # escolhe um item
random.choices(colours, k=3) # escolhe k itens (com reposição)
random.sample(colours, k=2)  # escolhe k itens (sem reposição)

numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers)      # embaralha no lugar, retorna None

Para resultados reproduzíveis (úteis em testes e ciência de dados), defina uma seed antes de gerar:

python
random.seed(42)
random.randint(1, 100)   # sempre o mesmo valor para seed 42

A mesma seed produz a mesma sequência toda vez, em qualquer máquina.

math

O módulo math adiciona operações matemáticas mais avançadas além dos operadores aritméticos básicos. Raízes quadradas, potências, logaritmos, trigonometria e valores especiais como pi e infinito estão todos aqui.

math fornece implementações em nível C de funções matemáticas padrão. Note que math.pow() sempre retorna um float, enquanto o operador ** do Python retorna int para bases e expoentes inteiros. math.log(x, base) calcula o logaritmo em qualquer base; math.log(x) calcula o logaritmo natural.

math envolve as funções da biblioteca C <math.h>. Estas são mais rápidas que implementações em Python puro e lidam com casos extremos (NaN, infinito) corretamente. math.isnan() e math.isinf() verificam valores especiais IEEE 754. Para números complexos, cmath fornece as funções correspondentes. Para matemática em nível de arrays, numpy é a ferramenta padrão.

python
import math

math.sqrt(25)        # 5.0
math.pow(2, 10)      # 1024.0 (igual a 2 ** 10 mas sempre retorna 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 a int() para positivos)

math.inf          # infinito
math.isnan(float("nan"))   # True
math.isinf(math.inf)       # True

datetime

O módulo datetime lida com datas e horários. datetime.now() retorna a data e hora atuais. strftime() formata como string. strptime() interpreta uma string como datetime. timedelta representa uma duração que você pode adicionar ou subtrair.

datetime, date e timedelta são as classes principais. strftime() formata um datetime como string usando códigos de formato. strptime() interpreta uma string dado um padrão de formato. timedelta suporta aritmética: você pode adicionar ou subtrair durações de datas e comparar datetimes com <, >, -.

Objetos datetime são ingênuos por padrão (sem fuso horário). Para datetimes com fuso horário, use datetime.now(tz=timezone.utc) ou datetime.fromisoformat() com um offset. strftime/strptime usam códigos de formato da biblioteca C; %f fornece microssegundos. Para medição de tempo de alta precisão, prefira time.perf_counter() em vez de datetime.now(). O módulo zoneinfo (Python 3.9+) fornece suporte a fusos horários IANA.

python
from datetime import datetime, date, timedelta

now   = datetime.now()           # data e hora atuais
today = date.today()             # apenas data atual

print(now.year, now.month, now.day)
print(now.hour, now.minute, now.second)

# Formatação
print(now.strftime("%Y-%m-%d"))           # "2024-01-15"
print(now.strftime("%d %B %Y, %H:%M"))   # "15 January 2024, 09:42"

# Parsing
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 comuns do strftime:

CódigoSignificadoExemplo
%YAno com 4 dígitos2024
%mMês (com zero à esquerda)01
%dDia (com zero à esquerda)15
%HHora (24h)09
%MMinuto42
%BNome completo do mêsJanuary

os e pathlib

pathlib é a forma moderna de trabalhar com caminhos de arquivos. Objetos Path permitem construir, inspecionar e navegar por caminhos usando o operador /. os dá acesso a variáveis de ambiente e operações de baixo nível do SO. Prefira pathlib para código novo.

pathlib.Path representa caminhos do sistema de arquivos como objetos com métodos para consultar e navegar. O operador / junta componentes de caminho de forma limpa, lidando automaticamente com separadores específicos do SO. os.environ é um objeto semelhante a um dict para variáveis de ambiente; os.environ.get("KEY", "default") é seguro para variáveis ausentes.

pathlib.Path é uma base abstrata com PurePosixPath e PureWindowsPath como implementações concretas para cada SO. Métodos como .glob(), .rglob() e .iterdir() retornam geradores. .stat() chama os.stat() e retorna um stat_result. Funções os.path aceitam tanto strings quanto objetos Path desde o Python 3.6. Prefira pathlib para código novo; use os.fspath() para converter Path em str ao chamar APIs que não aceitam Path.

python
from pathlib import Path

p = Path("data/reports")

p.exists()           # True se o caminho existir
p.is_dir()           # True se for um diretório
p.is_file()          # True se for um arquivo

p.mkdir(parents=True, exist_ok=True)   # cria diretórios

for f in p.glob("*.csv"):              # todos os arquivos CSV no diretório
    print(f.name)                      # apenas o nome do arquivo

report = p / "report_jan.csv"          # operador / junta caminhos
report.stem       # "report_jan" (nome sem extensão)
report.suffix     # ".csv"
report.parent     # Path("data/reports")

content = report.read_text()           # lê o conteúdo do arquivo diretamente
report.write_text("new content\n")    # escreve diretamente

Para o módulo os:

python
import os

os.getcwd()                        # diretório de trabalho atual
os.listdir(".")                    # lista o conteúdo do diretório
os.path.exists("data.txt")        # True se o caminho existir
os.path.join("data", "file.txt")  # "data/file.txt" (multiplataforma)
os.environ.get("HOME")            # lê uma variável de ambiente

Prefira pathlib para código novo. Use os quando precisar de variáveis de ambiente ou ao trabalhar com APIs mais antigas que esperam strings.

timeit

timeit mede quanto tempo o código leva para rodar. É útil quando você quer comparar duas abordagens e escolher a mais rápida. Execute o código muitas vezes para obter uma medição estável.

timeit.timeit(stmt, setup, number) mede o tempo de stmt executando-o number vezes e retornando o tempo total decorrido em segundos. A string setup é executada uma vez antes do loop cronometrado. Divida o resultado por number para obter o tempo por chamada. Mais repetições reduzem ruído do agendamento do sistema.

timeit desabilita o coletor de lixo durante a medição para reduzir ruído. Ele usa time.perf_counter() para medição de alta resolução. O parâmetro globals passa um namespace para a instrução cronometrada. Para microbenchmarks, timeit é a ferramenta padrão; para profiling onde o tempo é gasto em um programa maior, use cProfile.

python
import timeit

# Mede uma única instrução
timeit.timeit("sum(range(1000))", number=10000)

# Mede um bloco mais complexo
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 é quantas vezes repetir. Mais repetições resultam em uma medição mais estável.

string

O módulo string fornece constantes de string pré-construídas para letras, dígitos e pontuação. Útil quando você precisa verificar caracteres ou gerar strings aleatórias a partir de um alfabeto específico.

As constantes do módulo string (ascii_letters, digits, punctuation) são strings simples que você pode indexar, iterar ou usar com in. Combiná-las com random.choices() é a forma padrão de gerar tokens ou senhas aleatórias.

As constantes do módulo string são literais de string Python puros sem comportamento especial. Elas não são conjuntos, então in é O(n); para testes de pertencimento frequentes, use set(string.digits). string.Formatter e string.Template são a maquinaria subjacente para str.format() e substituição estilo $, respectivamente.

python
import string

string.ascii_lowercase   # "abcdefghijklmnopqrstuvwxyz"
string.ascii_uppercase   # "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
string.ascii_letters     # ambos combinados
string.digits            # "0123456789"
string.punctuation       # todos os caracteres de pontuação

Útil quando você precisa verificar caracteres ou gerar strings aleatórias:

python
import string, random

chars    = string.ascii_letters + string.digits
password = "".join(random.choices(chars, k=12))

Criando seus próprios módulos

Qualquer arquivo Python é um módulo. Para usá-lo a partir de outro arquivo, importe-o pelo nome do arquivo (sem .py). Você pode importar o módulo inteiro e usar seu conteúdo com notação de ponto, ou importar nomes específicos diretamente.

Quando o Python importa um módulo, ele executa o arquivo de cima para baixo uma vez e armazena em cache o resultado em sys.modules. Imports subsequentes do mesmo módulo retornam o objeto em cache sem re-executar o arquivo. Para projetos maiores, módulos são organizados em pacotes: diretórios com um arquivo __init__.py.

A resolução de import usa sys.path: uma lista de diretórios pesquisados em ordem. sys.path[0] é o diretório do script. A variável de ambiente PYTHONPATH adiciona diretórios extras no início. Pacotes requerem __init__.py (pode estar vazio) para serem reconhecidos. Imports relativos (from . import module) são válidos dentro de pacotes. importlib.reload() re-executa um módulo, mas referências existentes a objetos antigos não são atualizadas.

python
# utils.py
def clamp(value, lo, hi):
    return max(lo, min(value, hi))

PI = 3.14159
python
# main.py
import utils

utils.clamp(150, 0, 100)   # 100
utils.PI                    # 3.14159

from utils import clamp
clamp(50, 0, 100)           # 50

O Python encontra o módulo procurando no mesmo diretório do arquivo que está importando (e em alguns outros lugares). Para projetos maiores, módulos são organizados em pacotes: diretórios com um arquivo __init__.py.

__name__ == "__main__"

Quando o Python executa um arquivo diretamente, __name__ é definido como "__main__". Quando o mesmo arquivo é importado como módulo, __name__ é o nome do módulo. Esse padrão permite escrever código que roda quando você executa o arquivo diretamente, mas é ignorado quando o arquivo é importado por outro módulo.

if __name__ == "__main__": é a proteção padrão para código executável de módulo. Permite que um módulo seja tanto importável (expondo suas funções) quanto diretamente executável (com código de teste ou demo). Sem isso, importar o módulo executaria qualquer código de nível superior, o que quase nunca é desejado.

__name__ é definido pela maquinaria de import: "__main__" para o script de ponto de entrada, o nome pontuado do módulo caso contrário. A proteção evita que efeitos colaterais (código de inicialização, parsing de argumentos, execuções de teste) sejam executados na importação. Para ferramentas de linha de comando, colocar a lógica do ponto de entrada em uma função main() e chamá-la sob a proteção é o padrão idiomático.

python
# utils.py
def clamp(value, lo, hi):
    return max(lo, min(value, hi))

if __name__ == "__main__":
    # isto só roda quando você faz: python utils.py
    # não quando você faz: import utils
    print(clamp(150, 0, 100))   # 100

Esse é um padrão padrão para qualquer módulo que também seja útil como script autônomo.

Destaques da biblioteca padrão

Mais alguns módulos que vale a pena conhecer. Cada um resolve um problema comum que daria muito trabalho para implementar por conta própria.

A biblioteca padrão é extensa; os destaques abaixo são os que você encontrará com mais frequência em código de produção. Para uma referência completa, docs.python.org/3/library é a fonte oficial.

A biblioteca padrão é um conjunto curado de módulos bem testados e documentados. Antes de recorrer a um pacote de terceiros, verifique se a biblioteca padrão tem uma solução: functools, itertools, contextlib, dataclasses, typing e abc cada um fornece ferramentas que pacotes de terceiros frequentemente reinventam.

collections: tipos de containers especializados:

python
from collections import Counter, defaultdict, deque

Counter(["a", "b", "a", "c", "a"])   # Counter({'a': 3, 'b': 1, 'c': 1})
defaultdict(list)                      # dict que cria chaves ausentes automaticamente
deque([1, 2, 3], maxlen=5)            # append/pop rápido nas duas extremidades

itertools: ferramentas para trabalhar com iteráveis:

python
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: acesso ao interpretador Python:

python
import sys

sys.argv        # lista de argumentos de linha de comando
sys.exit(1)     # sai com um código de status
sys.version     # string da versão do Python

Pacotes de terceiros: além da biblioteca padrão, o pip instala pacotes da comunidade:

bash
pip install requests    # biblioteca HTTP
pip install pandas      # manipulação de dados
pip install numpy       # computação numérica

Pacotes de terceiros estão fora do escopo deste guia, mas o padrão é sempre o mesmo: pip install, depois import.

Na prática

Combinando random, string e datetime para gerar IDs únicos de jogos com timestamps:

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

Usando pathlib e datetime para encontrar arquivos em um diretório e reportar seus tamanhos:

python
from pathlib import Path
from datetime import datetime

def find_files(directory: str, pattern: str = "*.csv") -> list[Path]:
    return sorted(Path(directory).glob(pattern))

def timestamp() -> str:
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

files = find_files(".", "*.md")[:3]
print(f"[{timestamp()}] Found {len(files)} file(s)")
for f in files:
    size = f.stat().st_size if f.exists() else 0
    print(f"  {f.name} ({size} bytes)")

Lendo configuração da aplicação a partir de variáveis de ambiente com defaults tipados, e escrevendo entradas de log de acesso estruturadas como JSON delimitado por nova linha:

python
import os
import json
from datetime import datetime
from pathlib import Path

def load_env_config() -> dict:
    return {
        "debug":     os.environ.get("DEBUG", "false").lower() == "true",
        "port":      int(os.environ.get("PORT", "8080")),
        "log_level": os.environ.get("LOG_LEVEL", "INFO"),
    }

def write_access_log(method: str, path: str, status: int) -> None:
    log_dir = Path("logs")
    log_dir.mkdir(exist_ok=True)
    entry = {
        "ts":     datetime.now().isoformat(),
        "method": method,
        "path":   path,
        "status": status,
    }
    with open(log_dir / "access.jsonl", "a") as f:
        f.write(json.dumps(entry) + "\n")

config = load_env_config()
print(f"Starting on port {config['port']}, debug={config['debug']}")
write_access_log("GET", "/users", 200)

JSON delimitado por nova linha (.jsonl) é um formato comum de log: cada linha é um objeto JSON válido, o que torna fácil fazer streaming, anexar e parsear linha por linha sem carregar o arquivo inteiro.