Arquivos e exceções

A maioria dos programas que fazem trabalho real tocam o sistema de arquivos: lendo uma configuração, escrevendo resultados, carregando dados. E quando as coisas dão errado, Python lança uma exceção, um sinal de que algo inesperado aconteceu. Este capítulo cobre os dois: colocando dados dentro e fora de arquivos, e escrevendo código que trata erros com elegância em vez de falhar.
Abrindo arquivos
open() abre um arquivo e retorna um objeto do qual você pode ler ou escrever. Você diz a ele o caminho e o que quer fazer com o arquivo (ler, escrever ou acrescentar). Sempre feche um arquivo quando terminar; a declaração with faz isso automaticamente.
f = open("data.txt", "r") # "r" = ler
content = f.read()
f.close()O "r" é o modo:
| Modo | Significado |
|---|---|
"r" | Ler. O arquivo deve existir. Modo padrão. |
"w" | Escrever. Cria ou sobrescreve o arquivo. |
"a" | Acrescentar. Adiciona ao final sem apagar. |
"x" | Criar. Falha se o arquivo já existe. |
"r+" | Ler e escrever. |
"b" | Binário. Adicione a qualquer modo: "rb", "wb". |
Sempre chame .close() quando terminar. Esquecer deixa o arquivo aberto e pode perder dados que ainda estavam esperando para serem escritos. A forma confiável de lidar com isso é a declaração with.
open(path, mode) lhe entrega um objeto de arquivo, e o modo diz o que você quer fazer: "r" para ler, "w" para escrever (o que apaga o que estava lá), "a" para adicionar ao final. Perdi um arquivo para "w" uma vez ao alcançá-lo quando quis "a", então imagine o modo antes de digitá-lo. A declaração with
with open(...) gerencia o arquivo para você, fechando-o automaticamente quando o bloco indentado termina, mesmo se um erro acontecer. 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, garantidowith é a sintaxe de gerenciador de contexto do Python: executa código de configuração e limpeza para você, aqui abrindo o arquivo e fechando-o de forma confiável. Você não precisa saber como funciona internamente para usá-lo com open().
with open(...) as f abre o arquivo, deixa você trabalhar com f dentro do bloco indentado, então o fecha para você quando o bloco termina, mesmo se algo der errado no meio. Alcance por ele cada vez em vez de chamar .close() você mesmo, é uma coisa a menos para esquecer. Lendo arquivos
Três métodos para leitura. .read() carrega o arquivo inteiro como uma string. .readline() lê uma linha. Iterar diretamente sobre o objeto de arquivo lê linha por linha, que é a abordagem mais eficiente para arquivos grandes pois não carrega tudo na memória de uma vez.
with open("data.txt", "r") as f:
content = f.read() # arquivo inteiro como uma string
with open("data.txt", "r") as f:
first_line = f.readline() # uma linha por 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 que carregar tudo de uma vez:
with open("big_file.txt", "r") as f:
for line in f: # itere 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 de ler um arquivo grande.
.read() lhe dá o arquivo todo como uma string, .readline() lhe dá uma única linha. Quando o arquivo pudesse ser grande, faça loop com `for line in f`, ele lê uma linha por vez então você nunca carrega o lote todo em memória. O `.strip()` em cada linha limpa a quebra de linha final. Escrevendo arquivos
O modo "w" sobrescreve o arquivo inteiro se existir. O modo "a" adiciona ao final. .write() não adiciona uma quebra de linha automaticamente; inclua "\n" explicitamente ao final de cada linha. Para escrever múltiplas 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 inteiro se existir. "a" adiciona ao final.
f.write() não adiciona uma quebra de linha automaticamente, então inclua "\n" explicitamente. Para escrever múltiplas 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")"w" cria ou sobrescreve o arquivo, "a" adiciona ao final sem apagá-lo. .write() não adiciona uma quebra de linha para você, então coloque "\n" ao final de cada linha você mesmo. Para um lote de linhas, "\n".join(lines) as une primeiro. Exceções
Quando Python encontra um problema que não pode lidar, lança uma exceção: um erro que descreve o que deu errado e onde. Se você não a tratar, seu programa falha 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 acontece |
|---|---|
FileNotFoundError | open() não consegue encontrar o arquivo |
ValueError | Função recebe um valor do tipo certo mas conteúdo errado, por ex. int("abc") |
TypeError | Tipo completamente errado, por 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 esse 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 falhar. Seja específico sobre qual exceção você captura: capturar tudo com um except: nu 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: nu esconde bugs:
# ruim, captura tudo inclusive erros do programador
try:
result = do_something()
except:
pass
# bom, apenas captura o que você espera e realmente pode 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 bloco usando uma tupla. A parte as e lhe 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 capture 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 executa apenas se nenhuma exceção ocorreu. finally sempre executa, quer haja uma exceção ou não. finally é útil para limpeza que deve 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("Feito tentando carregar arquivo") # sempre executafinally é mais útil para limpeza (fechando conexões, liberando travas) mesmo quando você já está usando with para arquivos.
raise
Você pode lançar exceções você mesmo com raise. Isso é como você faz suas funções sinalizarem problemas claramente aos chamadores em vez de silenciosamente retornar um valor errado.
def divide(a, b):
if b == 0:
raise ValueError("Não posso dividir por zero")
return a / bIsso torna suas funções explícitas sobre o que esperam e sinaliza problemas claramente aos chamadores.
raise ExceptionType("message") deixa seu próprio código sinalizar um problema em voz alta em vez de continuar com um valor ruim. É como uma função diz "Não posso trabalhar com isso" então o chamador lida com isso. Claro e cedo bate uma resposta errada que mostra muito depois. Classes de exceção customizadas
Para programas maiores, você pode definir seus próprios tipos de exceção herdando de Exception. Isso deixa chamadores capturarem seus erros específicos separadamente de outros tipos de erros.
class SaldoInsuficienteError(Exception):
pass
class ContaBancaria:
def __init__(self, saldo):
self.saldo = saldo
def sacar(self, montante):
if montante > self.saldo:
raise SaldoInsuficienteError(
f"Não posso sacar {montante}, saldo é {self.saldo}"
)
self.saldo -= montantetry:
conta.sacar(1000)
except SaldoInsuficienteError as e:
print(f"Transação recusada: {e}")JSON
JSON é o formato que tudo fala: APIs, arquivos de configuração, exportações de dados. O módulo json do Python o trata diretamente. json.load() lê JSON de um arquivo para um Python dicionário ou lista. json.dump() escreve um dicionário ou lista de volta para um arquivo como JSON.
Leia JSON de um arquivo:
import json
with open("config.json", "r") as f:
config = json.load(f) # analisa JSON em um Python dict/list
print(config["setting"])Escreva JSON para um arquivo:
import json
data = {"name": "João", "score": 87, "active": True}
with open("output.json", "w") as f:
json.dump(data, f, indent=2) # indent= torna legível para humanosMapeamento de tipo JSON para Python:
| JSON | Python |
|---|---|
object {} | dict |
array [] | list |
string "" | str |
| number | int or float |
true / false | True / False |
null | None |
Para converter entre strings JSON e objetos Python sem tocar um arquivo:
import json
# string para Python
data = json.loads('{"name": "João", "score": 87}')
# Python para string
text = json.dumps({"name": "João", "score": 87}, indent=2)json.load() lê de um objeto de arquivo. json.loads() (com um "s") lê de uma string.
json.load(f) transforma JSON em um arquivo em Python dicts e lists, json.dump(data, f) escreve-os de volta. As versões `s`, `json.loads` e `json.dumps`, fazem o mesmo para uma string em vez de um arquivo. Adicione `indent=2` quando quer a saída legível. Na prática
Um padrão de salvar/carregar para um pequeno jogo: escreva estado para JSON, carregue-o de volta na próxima execução, e caia em padrões se nenhum arquivo de salvamento existir ainda:
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 salvamento encontrado, começando do zero.")
return {"name": "Player", "score": 0, "level": 1}
state = load_game()
state["score"] += 50
save_game(state)
