Arquivos e exceções
A maioria dos programas que faz trabalho real interage com o sistema de arquivos: lê uma configuração, escreve resultados, carrega dados. E quando algo dá errado, Python lança uma exceção, um sinal de que algo inesperado aconteceu. Este capítulo cobre os dois temas: colocar dados dentro e fora de arquivos, e escrever código que trata erros de forma elegante em vez de travar.
Abrindo arquivos
open() abre um arquivo e retorna um objeto do qual você pode ler ou no qual pode escrever. Você informa o caminho e o que deseja fazer com o arquivo (ler, escrever ou anexar). Sempre feche um arquivo quando terminar; a instrução with faz isso automaticamente.
f = open("data.txt", "r") # "r" = leitura
content = f.read()
f.close()O "r" é o modo:
| Modo | Significado |
|---|---|
"r" | Leitura. O arquivo deve existir. Modo padrão. |
"w" | Escrita. Cria ou sobrescreve o arquivo. |
"a" | Anexar. Adiciona ao final sem apagar. |
"x" | Criar. Falha se o arquivo já existir. |
"r+" | Leitura e escrita. |
"b" | Binário. Adicione a qualquer modo: "rb", "wb". |
Sempre chame .close() quando terminar. Esquecer disso deixa o arquivo bloqueado e pode causar corrupção de dados. A forma confiável de lidar com isso é a instrução with.
A instrução with
with open(...) gerencia o arquivo para você, fechando-o automaticamente quando o bloco indentado termina, mesmo que ocorra um erro. Sempre use with open(...) em vez de open()/close() manual. É mais seguro e é o padrão.
with open("data.txt", "r") as f:
content = f.read()
# f é fechado aqui, garantidamenteO que o with faz?
with é a sintaxe de gerenciador de contexto do Python. Ele chama código de configuração e finalização para você; neste caso, abrindo e fechando o arquivo de forma confiável. Você não precisa saber como funciona internamente. Apenas use-o com open().
Lendo arquivos
Três métodos para leitura. .read() carrega o arquivo inteiro como uma única string. .readline() lê uma linha. Iterar diretamente sobre o objeto de arquivo lê linha por linha, que é a abordagem mais eficiente para arquivos grandes, já que não carrega tudo na memória de uma vez.
with open("data.txt", "r") as f:
content = f.read() # arquivo inteiro como uma única string
with open("data.txt", "r") as f:
first_line = f.readline() # uma linha de cada vez
with open("data.txt", "r") as f:
lines = f.readlines() # lista de linhas, cada uma terminando em "\n"Para arquivos grandes, ler linha por linha é mais eficiente do que carregar tudo de uma vez:
with open("big_file.txt", "r") as f:
for line in f: # itera o arquivo diretamente, eficiente em memória
print(line.strip()) # strip() remove a quebra de linha finalIterar diretamente sobre o objeto de arquivo (for line in f) é a forma mais eficiente e idiomática de ler um arquivo grande.
Escrevendo arquivos
O modo "w" sobrescreve o arquivo inteiramente se ele existir. O modo "a" adiciona ao final. .write() não adiciona uma quebra de linha automaticamente; inclua "\n" explicitamente no final de cada linha. Para escrever várias linhas de uma vez, junte-as com "\n".join().
with open("output.txt", "w") as f:
f.write("Olá, mundo\n")
with open("output.txt", "a") as f:
f.write("Outra linha\n")"w" sobrescreve o arquivo inteiramente se ele existir. "a" adiciona ao final.
f.write() não adiciona uma quebra de linha automaticamente, então inclua "\n" explicitamente. Para escrever várias linhas de uma vez:
lines = ["Linha um", "Linha dois", "Linha três"]
with open("output.txt", "w") as f:
f.write("\n".join(lines) + "\n")Exceções
Quando Python encontra um problema que não consegue lidar, ele lança uma exceção: um erro que descreve o que deu errado e onde. Se você não tratá-lo, seu programa trava e imprime um traceback. A tabela abaixo mostra as exceções mais comuns que você encontrará.
Exceções comuns que você encontrará:
| Exceção | Quando ocorre |
|---|---|
FileNotFoundError | open() não consegue encontrar o arquivo |
ValueError | A função recebe um valor do tipo certo mas conteúdo errado, ex.: int("abc") |
TypeError | Tipo totalmente errado, ex.: "hello" + 5 |
KeyError | Chave de dicionário não existe |
IndexError | Índice de lista fora do intervalo |
ZeroDivisionError | Divisão por zero |
AttributeError | Objeto não tem aquele atributo ou método |
try / except
Envolva código que pode falhar em um bloco try. Se uma exceção ocorrer, o bloco except correspondente a trata em vez de travar. Seja específico sobre qual exceção você captura: capturar tudo com um except: simples esconde bugs reais.
try:
value = int("abc")
except ValueError:
print("Isso não é um número válido")Seja específico sobre qual exceção você captura. Capturar todas as exceções com um except: simples esconde bugs:
# ruim, captura tudo incluindo erros do programador
try:
result = do_something()
except:
pass
# bom, captura apenas o que você espera e pode realmente tratar
try:
result = do_something()
except FileNotFoundError:
print("Arquivo não encontrado")Capturando múltiplas exceções
Você pode tratar diferentes tipos de erro em blocos except separados, ou capturar vários tipos em um único bloco usando uma tupla. A parte as e dá acesso à mensagem de erro.
try:
data = int(user_input)
result = 100 / data
except ValueError:
print("Não é um número")
except ZeroDivisionError:
print("Não posso dividir por zero")Ou capturar múltiplos em uma tupla:
except (ValueError, ZeroDivisionError) as e:
print(f"Erro de entrada: {e}")as e vincula o objeto de exceção a um nome para que você possa inspecionar a mensagem.
else e finally
else roda apenas se nenhuma exceção ocorrer. finally sempre roda, independentemente de ter ocorrido ou não uma exceção. finally é útil para limpeza que precisa acontecer não importa o quê.
try:
with open("data.txt") as f:
content = f.read()
except FileNotFoundError:
print("Arquivo não encontrado, usando padrões")
content = ""
else:
print("Arquivo carregado com sucesso")
finally:
print("Tentativa de carregar arquivo concluída") # sempre rodafinally é mais útil para limpeza (fechar conexões, liberar locks) mesmo quando você já está usando with para arquivos.
raise
Você mesmo pode lançar exceções com raise. É assim que você faz suas funções sinalizarem problemas claramente aos chamadores em vez de retornar silenciosamente um valor errado.
def divide(a, b):
if b == 0:
raise ValueError("Não é possível dividir por zero")
return a / bIsso torna suas funções explícitas sobre o que esperam e sinaliza problemas claramente aos chamadores.
Classes de exceção personalizadas
Para programas maiores, você pode definir seus próprios tipos de exceção herdando de Exception. Isso permite que chamadores capturem seus erros específicos separadamente de outros tipos de erros.
class InsufficientFundsError(Exception):
pass
class BankAccount:
def __init__(self, balance):
self.balance = balance
def withdraw(self, amount):
if amount > self.balance:
raise InsufficientFundsError(
f"Não é possível sacar {amount}, saldo é {self.balance}"
)
self.balance -= amounttry:
account.withdraw(1000)
except InsufficientFundsError as e:
print(f"Transação recusada: {e}")JSON
JSON é o formato que todo mundo entende: APIs, arquivos de configuração, exportações de dados. O módulo json do Python lida com ele diretamente. json.load() lê JSON de um arquivo em um dict ou list do Python. json.dump() escreve um dict ou list do Python em um arquivo como JSON.
Ler JSON de um arquivo:
import json
with open("config.json", "r") as f:
config = json.load(f) # analisa JSON em um dict/list do Python
print(config["setting"])Escrever JSON em um arquivo:
import json
data = {"name": "Ana", "score": 87, "active": True}
with open("output.json", "w") as f:
json.dump(data, f, indent=2) # indent= torna legível por humanosMapeamento de tipos JSON para Python:
| JSON | Python |
|---|---|
objeto {} | dict |
array [] | list |
string "" | str |
| número | int ou float |
true / false | True / False |
null | None |
Para converter entre strings JSON e objetos Python sem mexer em um arquivo:
import json
# string para Python
data = json.loads('{"name": "Ana", "score": 87}')
# Python para string
text = json.dumps({"name": "Ana", "score": 87}, indent=2)json.load() lê de um objeto de arquivo. json.loads() (com um "s") lê de uma string.
Na prática
Um padrão salvar/carregar para um jogo simples: escrever o estado em JSON, carregá-lo de volta na próxima execução e usar padrões caso ainda não exista arquivo de save:
import json
SAVE_FILE = "save_game.json"
def save_game(player_data: dict) -> None:
with open(SAVE_FILE, "w") as f:
json.dump(player_data, f, indent=2)
print("Jogo salvo.")
def load_game() -> dict:
try:
with open(SAVE_FILE, "r") as f:
return json.load(f)
except FileNotFoundError:
print("Nenhum arquivo de save encontrado, começando do zero.")
return {"name": "Player", "score": 0, "level": 1}
state = load_game()
state["score"] += 50
save_game(state)
