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

Módulos e a biblioteca padrão ​

O Python vem com uma coleção enorme de ferramentas prontas para usar: aleatoriedade, matemática, datas, caminhos de arquivo 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 Arquivos e exceções. Este capítulo cobre imports em detalhes e apresenta as partes mais úteis da biblioteca padrão.

A biblioteca padrão do Python oferece 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 os módulos, compila-os se necessário e os armazena em cache em sys.modules, de modo que só são carregados uma vez.

Um import realiza trabalho de verdade: o Python localiza o módulo, executa-o uma vez e então armazena o resultado em cache em sys.modules (o dicionário de todos os módulos já carregados), de modo que um segundo import do mesmo nome é apenas uma consulta rápida, não uma nova execução. import requests associa o objeto do módulo ao nome requests no seu namespace atual (a tabela de nomes visíveis naquele ponto do código). from requests import get faz a mesma carga, mas associa somente get. As partes que realmente valem a pena em projetos reais são onde o Python procura os módulos (sys.path), como diretórios se tornam pacotes importáveis (__init__.py) e como evitar imports circulares — tudo isso é abordado a seguir.

Importando módulos ​

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

import module associa o objeto do módulo ao nome module no escopo atual. from module import name associa apenas name. Aliases (import module as alias) são comuns com bibliotecas de terceiros. Evite from module import *: isso poluí o namespace e deixa pouco claro de onde vêm os nomes.

from module import name não pula nenhuma etapa: o Python ainda carrega e executa o módulo inteiro, e então associa apenas o nome que você pediu. A armadilha que vale conhecer é o import circular, quando dois módulos tentam importar um ao outro enquanto ainda estão sendo carregados, de modo que um deles vê um módulo pela metade e um nome está faltando. As soluções mais comuns são mover o import problemático para dentro da função que precisa dele (assim ele só é executado depois que os dois módulos terminam de carregar) ou extrair o código compartilhado para um terceiro módulo que ambos possam importar. Quando você precisa importar um módulo cujo nome só é conhecido em tempo de execução, importlib.import_module("nome") aceita o nome como uma string.

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 usá-los diretamente:

python
from math import sqrt, pi

sqrt(16)    # 4.0 (não precisa do prefixo "math.")
pi          # 3.141592653589793

Dê um alias a um módulo ou nome 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

Aliases 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 legível.

JunoImportando módulosimport math traz o módulo inteiro, e então você acessa o conteúdo com um ponto: math.sqrt(16). from math import sqrt pega um único nome para que você possa dispensar o prefixo. A palavra-chave as renomeia um import longo, mas para coisas da biblioteca padrão o nome completo se lê melhor.
JunoImportando módulosimport module associa o objeto do módulo, from module import name associa um único nome, import module as alias renomeia. Evite from module import *: isso despeja todos os nomes no seu escopo e esconde de onde cada um veio.
JunoImportando módulosfrom module import name ainda carrega o módulo inteiro, só mantém um único nome. A dor de cabeça que você vai encontrar em algum momento é o import circular, quando dois módulos importam um ao outro no meio do carregamento: mova aquele import para dentro da função, ou separe o código compartilhado em um terceiro módulo. importlib.import_module() está ali para quando o nome só é conhecido em tempo de execução.

random ​

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

O random usa um gerador de números pseudoaleató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 local e retorna None.

O random produz números pseudoaleatórios: eles parecem aleatórios, mas vêm de uma fórmula fixa determinada por um valor inicial (a seed), de modo que a mesma seed gera a mesma sequência em qualquer máquina — é isso que torna um teste reproduzível. A regra que importa em produção: nunca use random para algo que uma pessoa possa explorar, como senhas, tokens de sessão ou códigos de redefinição de senha. Ele é previsível por design, então um atacante que veja saída suficiente pode adivinhar o resto. Para esses casos, use o módulo secrets, que extrai aleatoriedade segura do sistema operacional. random.SystemRandom() oferece essa mesma fonte segura por trás dos nomes de métodos familiares do random, se você preferir não trocar de API.

python
import random

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

colours = ["vermelho", "verde", "azul"]
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 local, retorna None

Para resultados reproduzíveis (útil 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 a seed 42

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

Junorandomrandom.choice() escolhe um item de uma lista, random.randint(1, 10) dá um número inteiro dentro de um intervalo, incluindo o final. Quer os mesmos resultados a cada execução, digamos para um teste? Chame random.seed() antes e a sequência fica fixa.
Junorandom.choice() escolhe um, .choices() escolhe com reposição, .sample() sem reposição. .shuffle() reordena a lista no local e retorna None, então nunca escreva nums = random.shuffle(nums). A seed fixa a sequência inteira, e é assim que você torna um teste aleatório repetível.
Junorandomrandom é pseudoaleatório e previsível por design, então serve bem para jogos e testes, mas é errado para qualquer coisa que um atacante possa explorar. Use secrets para tokens, senhas e códigos de redefinição. Reserve random.seed() para os casos reproduzíveis onde você realmente quer que a sequência se repita.

math ​

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

O math oferece implementações em nível de C das funções matemáticas padrão. Observe 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.

O detalhe que costuma pegar no código real é nan (abreviação de "not a number", o float que você obtém de resultados indefinidos como 0/0 ou uma conversão que falhou) e inf (infinito). Um nan nunca é igual a nada, nem mesmo a si mesmo, então x == float("nan") é sempre False. Isso significa que você não pode testá-lo com ==; use math.isnan(x), e math.isinf(x) para infinito. Outro hábito que vale manter: prefira math.isclose(a, b) a a == b ao comparar dois floats, já que o arredondamento de ponto flutuante torna a igualdade exata pouco confiável. Para matemática sobre arrays inteiros de números, em vez de um por vez, o numpy é a ferramenta padrão, embora esteja fora do escopo deste guia.

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 na 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
Junomathmath cobre tudo além de + - * /: raízes quadradas, potências, logs, trigonometria, além de constantes como math.pi. Recorra a ele no momento em que um cálculo precisar de mais do que os operadores básicos, e você não terá que escrever a fórmula você mesmo.
Junomathmath tem raízes quadradas, logs e trigonometria. Uma coisa para observar: math.pow(2, 10) sempre retorna um float, enquanto 2 ** 10 permanece int para entradas inteiras. math.log(x, base) aceita qualquer base, math.log(x) é o log natural.
Junomath As armadilhas são nan e inf: nan nunca é igual a si mesmo, então teste com math.isnan(), não com ==. E compare floats com math.isclose() em vez de ==, já que o arredondamento torna a igualdade exata pouco confiável. Para arrays inteiros de números, esse é território do numpy.

datetime ​

O módulo datetime lida com datas e horas. datetime.now() fornece a data e hora atuais. strftime() formata isso como uma string. strptime() converte uma string em um datetime. timedelta representa uma duração que você pode somar ou subtrair.

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

A armadilha em produção é que um simples datetime.now() é naive (ingênuo): ele não tem fuso horário associado, então não sabe de fato qual parte do mundo representa. Dois datetimes naive de máquinas diferentes parecem comparáveis, mas silenciosamente não são, e essa ambiguidade causa bugs de horas erradas. A solução é trabalhar com datetimes aware (que carregam um fuso horário) e padronizar em UTC para tudo que é armazenado ou enviado entre sistemas: datetime.now(timezone.utc). Converta para o horário local somente na ponta final, quando for exibir para uma pessoa. Use o módulo zoneinfo para fusos nomeados como "America/Sao_Paulo", que trata automaticamente as mudanças de horário de verão. Mais uma regra: nunca meça tempo decorrido com diferenças de datetime.now(), porque o relógio do sistema pode saltar (uma sincronização ou uma alteração manual); use time.perf_counter(), um contador que só avança, para medir durações.

python
from datetime import datetime, date, timedelta

now = datetime.now()             # data e hora atuais
today = date.today()             # apenas a 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"

# Conversão de string
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 de 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
Junodatetimedatetime.now() fornece a data e hora atuais. strftime() transforma isso em uma string com códigos como %Y-%m-%d, e strptime() faz o caminho contrário, convertendo uma string de volta. timedelta é uma duração que você pode somar ou subtrair, então today + timedelta(days=1) é amanhã.
Junodatetimedatetime, date e timedelta são o núcleo. strftime() formata com códigos, strptime() converte com um padrão correspondente. Datas fazem aritmética: subtraia duas e você obtém um timedelta, some um timedelta para avançar ou retroceder.
Junodatetime Um simples datetime.now() é naive, sem fuso horário, o que quebra silenciosamente assim que duas máquinas entram em cena. Armazene e transmita em UTC com datetime.now(timezone.utc), converta para o horário local só na exibição, e use zoneinfo para fusos nomeados. E meça tempo decorrido com time.perf_counter(), nunca com diferenças de now(), já que o relógio do sistema pode saltar.

os e pathlib ​

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

pathlib.Path representa caminhos do sistema de arquivos como objetos com métodos para consulta e navegação. O operador / junta componentes de caminho de forma limpa, tratando automaticamente os separadores específicos de cada sistema operacional. os.environ é um objeto parecido com dict para variáveis de ambiente; os.environ.get("KEY", "default") é seguro para variáveis ausentes.

Path se adapta ao sistema operacional automaticamente, então o mesmo código junta caminhos com o separador correto em qualquer máquina: escreva Path("data") / "file.csv", nunca strings montadas à mão como "data/" + name, que quebram no Windows. Duas observações práticas. Primeiro, .glob(), .rglob() e .iterdir() retornam geradores (eles produzem entradas uma a uma, em vez de montar uma lista completa), então envolva-os em list() se precisar percorrer duas vezes ou saber a quantidade de antemão; para um diretório enorme, iterar de forma lenta é justamente a vantagem. Segundo, algumas bibliotecas mais antigas ainda esperam uma string simples em vez de um objeto Path; quando uma delas rejeitar um Path, passe str(path) e siga adiante. Para ler uma variável de ambiente, sempre use os.environ.get("KEY", "default") em vez de os.environ["KEY"], porque a forma com índice lança KeyError quando a variável não está definida, o que é o caso comum em uma implantação recém-feita.

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 os diretórios

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

report = p / "report_jan.csv"          # o operador / junta caminhos
report.stem       # "report_jan" (nome sem a 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 trabalhar com APIs mais antigas que esperam strings.

Junoos e pathlibpathlib.Path trata um caminho de arquivo como um objeto que você pode manipular: .exists(), .read_text(), .write_text(), .glob(). O operador / junta as partes, então p / "report.csv" se lê como um caminho de verdade. Prefira pathlib ao antigo os.path em código novo.
Junoos e pathlibPath junta com / e trata o separador do sistema operacional para você, então os caminhos permanecem portáveis. os.environ é parecido com dict para variáveis de ambiente, e os.environ.get("KEY", "default") é a leitura segura, ela não explode com uma chave ausente.
Junoos e pathlib Construa caminhos com Path e /, nunca strings coladas que quebram no Windows. .glob() e afins devolvem geradores, então use list() se precisar percorrer duas vezes. E leia variáveis de ambiente com os.environ.get(key, default): a forma com colchetes lança KeyError no instante em que uma variável não está definida, exatamente o caso de uma implantação recém-feita.

timeit ​

timeit mede quanto tempo um código leva para executar. É ú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 o ruído causado pelo agendamento do sistema.

timeit é a ferramenta certa para um microbenchmark: cronometrar um trecho pequeno isoladamente, executado muitas vezes para que o ruído de agendamento se equilibre na média. Ele reduz esse ruído para você, mas uma única execução ainda é pouco confiável, então use o melhor de várias repetições em vez de um único número. O erro a evitar é usá-lo para encontrar uma lentidão em um programa inteiro: ele só mede o trecho que você passa a ele, então não consegue dizer qual função está realmente lenta. Para isso, use cProfile, que executa o seu programa real e relata para onde o tempo foi. Regra geral: timeit para comparar duas implementações, cProfile para encontrar o gargalo em primeiro lugar.

python
import timeit

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

# Mede o tempo de 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} segundos para 10.000 execuções")

number é quantas vezes repetir. Mais repetições dão uma medição mais estável.

Junotimeittimeit.timeit() mede o tempo de um trecho de código executando-o repetidamente, e então você divide pelo número de execuções para obter um valor estável por chamada. É assim que você resolve "qual dos dois é mais rápido" em vez de chutar. Uma única execução tem ruído demais para confiar, então deixe rodar em loop.
Junotimeittimeit.timeit(stmt, setup, number) executa stmt aquele número de vezes e retorna o total em segundos; a string setup executa uma vez antes do loop. Divida por number para obter o tempo por chamada, e aumente as repetições para afogar o ruído do agendamento.
Junotimeittimeit é para microbenchmarks: comparar dois trechos pequenos, o melhor de várias execuções em vez de uma única cheia de ruído. Ele não consegue dizer qual função em um programa real está lenta, já que só mede o que você fornece a ele. Use cProfile para encontrar o gargalo, depois timeit para comparar correções.

string ​

O módulo string fornece constantes de string já prontas 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 comuns 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.

Essas constantes são strings comuns, então verificar char in string.ascii_letters percorre a string inteira a cada vez. Essa busca é O(n): o trabalho cresce com o comprimento da string, o que é tranquilo uma vez, mas custoso em um loop apertado. Se você está testando pertencimento repetidamente, construa um set uma única vez (letters = set(string.ascii_letters)) e verifique contra ele, já que uma busca em um set permanece rápida independentemente de quantas entradas ele tenha. A outra ferramenta útil aqui é string.Template, que faz substituição simples do tipo $name: quando um template vem de uma entrada do usuário ou de um arquivo de configuração, ela é mais segura do que passar um texto não confiável para um f-string ou str.format(), porque não consegue acessar suas variáveis nem chamar métodos.

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))
Junostring O módulo string lhe dá conjuntos de caracteres já prontos: string.ascii_letters, string.digits, string.punctuation. São strings comuns, então você pode percorrê-las ou amostrar delas. Útil sempre que você precisar de um alfabeto conhecido para construir ou verificar contra ele.
Junostringstring.ascii_letters, string.digits e afins são strings comuns que você pode indexar, iterar ou testar com in. Combine-as com random.choices() e você tem a receita padrão para um token ou senha aleatória. Vale notar que random não é seguro para segredos de verdade.
Junostring As constantes são strings comuns, então char in string.ascii_letters percorre tudo a cada chamada: construa um set uma vez se você estiver verificando dentro de um loop. E quando um template vem de entrada do usuário, string.Template com $name é mais seguro do que um f-string, já que não consegue acessar suas variáveis.

Criando seus próprios módulos ​

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

Quando o Python importa um módulo, ele executa o arquivo do início ao fim uma vez e armazena o resultado em cache em sys.modules. Importações subsequentes do mesmo módulo retornam o objeto em cache sem executar o arquivo novamente. Para projetos maiores, os módulos são organizados em pacotes: diretórios com um arquivo __init__.py.

Quando você escreve import utils, o Python procura nos diretórios de sys.path em ordem, começando pela pasta do script que você executou. Essa é a origem do bug de import mais comum do tipo "funciona na minha máquina": execute um arquivo a partir de um diretório de trabalho diferente e a pasta que ele espera pode não estar no path, então o import falha. A solução confiável é executar o projeto como um pacote (python -m mypackage.main), em vez de apontar o Python para um arquivo solto. Um pacote é um diretório de módulos com um arquivo __init__.py (ele pode estar vazio; a sua presença é o que diz ao Python que a pasta é importável). Dentro de um pacote, um import relativo como from . import helpers significa "a partir deste mesmo pacote", o que mantém os imports funcionando mesmo se o pacote for renomeado ou movido. Um detalhe delicado: importlib.reload() executa o módulo de novo, mas quaisquer nomes que você já tenha importado continuam apontando para a versão antiga, então recarregar não é confiável para nada além de um experimento rápido no REPL.

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 mais alguns lugares). Para projetos maiores, os módulos são organizados em pacotes: diretórios com um arquivo __init__.py.

JunoCriando seus próprios módulos Qualquer arquivo .py já é um módulo: importe-o pelo nome do arquivo sem o .py, e suas funções e variáveis ficam à sua disposição. import utils e depois utils.clamp(...), ou traga um único nome com from utils import clamp. É assim que você divide um programa em crescimento entre vários arquivos.
JunoCriando seus próprios módulos Importar um módulo o executa do início ao fim uma vez, e então o armazena em cache em sys.modules, então imports posteriores retornam o mesmo objeto sem executar o arquivo de novo. Por isso, mantenha o código de nível superior leve, qualquer coisa pesada roda no momento do import. Agrupe módulos relacionados em um pacote: uma pasta com um __init__.py.
JunoCriando seus próprios módulos Imports são resolvidos através de sys.path, começando pela pasta do próprio script, e é por isso que executar um arquivo solto a partir do diretório errado quebra o import. Execute-o como um pacote em vez disso: python -m pkg.main. E não confie em importlib.reload() além de testes rápidos no REPL, já que os nomes que você já importou continuam apontando para a versão antiga.

__name__ == "__main__" ​

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

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

O Python define __name__ como "__main__" para o arquivo que você realmente executou, e como o próprio nome do módulo para qualquer coisa importada. A guarda existe para impedir que efeitos colaterais de tempo de import (código que faz algo quando o módulo carrega, como analisar argumentos, abrir uma conexão ou executar uma demonstração) disparem no instante em que outro arquivo importa você. O padrão que se sustenta: coloque o trabalho de verdade em uma função main() e chame-a sob a guarda, assim quem importa recebe as funções sem a inicialização, e o arquivo ainda executa de forma limpa por conta própria. Pular a guarda é o motivo clássico de uma suíte de testes rodar misteriosamente o código de demonstração de alguém no instante em que importa o módulo.

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

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

Este é um padrão comum para qualquer módulo que também é útil como script independente.

Juno__name__ == '__main__'if __name__ == "__main__": permite que um arquivo seja tanto um módulo importável quanto um script executável. O código sob a guarda executa quando você faz python utils.py, e é ignorado quando outro arquivo faz import utils. Útil para encaixar uma demonstração ou teste rápido no final de um módulo.
Juno__name__ == '__main__'__name__ só é "__main__" no arquivo que você executou diretamente. A guarda impede que seu código de demonstração ou teste execute quando o módulo é importado, o que caso contrário aconteceria silenciosamente. É a forma padrão de fazer um arquivo funcionar tanto como biblioteca quanto como script.
Juno__name__ == '__main__' A guarda impede efeitos colaterais de tempo de import, análise de argumentos, demonstrações, conexões, de disparar quando alguém importa você. Coloque o trabalho em main() e chame-a sob a guarda: quem importa recebe as funções, o arquivo ainda executa por conta própria. Pule-a e observe uma suíte de testes rodar sua demonstração no instante em que importa o módulo.

Destaques da biblioteca padrão ​

Mais alguns módulos que vale conhecer. Cada um resolve um problema comum que exigiria bastante 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 selecionado de módulos bem testados e documentados. Antes de recorrer a um pacote de terceiros, verifique se a biblioteca padrão já tem uma solução: functools, itertools, contextlib, dataclasses, typing e abc cada um oferece ferramentas que pacotes de terceiros frequentemente reinventam.

collections: tipos de contêiner 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)            # inserção/remoção rápida em ambas as 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 com a 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 jogo 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 relatar 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 a configuração do app a partir de variáveis de ambiente com valores padrão tipados, e escrevendo entradas de log de acesso estruturadas como JSON delimitado por 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 linha (.jsonl) é um formato de log comum: cada linha é um objeto JSON válido, o que facilita transmitir, acrescentar e analisar linha por linha sem precisar carregar o arquivo inteiro.