Arquivos e exceções
A maioria dos programas que fazem algo de fato útil interage com o sistema de arquivos: lendo uma configuração, escrevendo resultados, carregando dados. E quando algo dá errado, o Python levanta uma exceção, um sinal de que algo inesperado aconteceu. Este capítulo cobre os dois lados: como fazer dados entrarem e saírem de arquivos, e como escrever código que trata erros com elegância em vez de simplesmente 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 quer fazer com o arquivo (ler, escrever ou acrescentar). Sempre fecha 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 precisa existir. Modo padrão. |
"w" | Escrita. Cria ou sobrescreve o arquivo. |
"a" | Acrescentar. Adiciona ao final sem apagar. |
"x" | Criação. 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 isso deixa o arquivo aberto e pode fazer você perder dados que ainda estavam esperando para ser gravados. A forma confiável de lidar com isso é a instrução with.
open(path, mode) te 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 já estava lá), "a" para adicionar ao final. Já perdi um arquivo por usar "w" quando queria "a", então visualize o modo antes de digitá-lo. A instrução with
with open(...) gerencia o arquivo para você, fechando-o automaticamente quando o bloco indentado termina, mesmo se ocorrer um erro. Sempre use with open(...) em vez de open()/close() manuais. É mais seguro e é o padrão.
with open("data.txt", "r") as f:
content = f.read()
# f é fechado aqui, garantidamentewith é a sintaxe de gerenciador de contexto do Python: ele executa código de preparação e finalização para você, aqui abrindo o arquivo e fechando-o de forma confiável. Você não precisa saber como isso funciona internamente para usá-lo com open().
with open(...) as f abre o arquivo, deixa você trabalhar com f dentro do bloco indentado, e depois o fecha para você quando o bloco termina, mesmo se algo der errado no meio do caminho. Use sempre isso em vez de chamar .close() você mesmo, é uma coisa menos para esquecer. 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, o 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 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 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 de ler um arquivo grande.
.read() te dá o arquivo inteiro como uma string, .readline() te dá uma única linha. Quando o arquivo pode ser grande, use um laço com for line in f, pois ele lê uma linha por vez e nunca carrega tudo na memória. O .strip() em cada linha remove a quebra de linha final. Escrevendo arquivos
O modo "w" sobrescreve o arquivo inteiro se ele 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 várias linhas de uma vez, junte-as com "\n".join().
with open("output.txt", "w") as f:
f.write("Hello, world\n")
with open("output.txt", "a") as f:
f.write("Another line\n")"w" sobrescreve o arquivo inteiro 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 = ["Line one", "Line two", "Line three"]
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 acrescente "\n" em cada linha você mesmo. Para um conjunto de linhas, "\n".join(lines) as junta antes. Exceções
Quando o Python encontra um problema que não consegue tratar, ele levanta uma exceção: um erro que descreve o que deu errado e onde. Se você não a tratar, seu programa trava e imprime um traceback. A tabela abaixo mostra as exceções mais comuns que você vai encontrar.
Exceções comuns que você vai encontrar:
| Exceção | Quando acontece |
|---|---|
FileNotFoundError | open() não encontra o arquivo |
ValueError | A função recebe um valor do tipo certo, mas com conteúdo errado, ex.: int("abc") |
TypeError | Tipo completamente errado, ex.: "hello" + 5 |
KeyError | A chave do dicionário não existe |
IndexError | Índice da lista fora do intervalo |
ZeroDivisionError | Divisão por zero |
AttributeError | O objeto não tem esse atributo ou método |
FileNotFoundError, ValueError, KeyError, TypeError. Se não tratadas, elas interrompem o programa e imprimem um traceback, que parece alarmante, mas na verdade é um mapa apontando exatamente para onde algo quebrou. try / except
Coloque código que pode falhar dentro de um bloco try. Se uma exceção ocorrer, o bloco except correspondente a trata em vez de deixar o programa travar. Seja específico sobre qual exceção você captura: capturar tudo com um except: sem tipo esconde bugs reais.
try:
value = int("abc")
except ValueError:
print("That's not a valid number")Seja específico sobre qual exceção você captura. Capturar todas as exceções com um except: sem tipo esconde bugs:
# ruim, captura tudo, incluindo erros de programação
try:
result = do_something()
except:
pass
# bom, captura apenas o que você espera e consegue de fato tratar
try:
result = do_something()
except FileNotFoundError:
print("File not found")try, e o except correspondente executa em vez do programa travar. Nomeie a exceção que você espera, como ValueError, em vez de um except: sem tipo, que captura tudo e esconde bugs reais de você. Capture o que você realmente consegue lidar, e deixe o resto se mostrar. Capturando múltiplas exceções
Você pode tratar tipos diferentes de erro em blocos except separados, ou capturar vários tipos em um único bloco usando uma tupla. A parte as e te dá acesso à mensagem do erro.
try:
data = int(user_input)
result = 100 / data
except ValueError:
print("Not a number")
except ZeroDivisionError:
print("Can't divide by zero")Ou capture várias em uma tupla:
except (ValueError, ZeroDivisionError) as e:
print(f"Input error: {e}")as e vincula o objeto de exceção a um nome para que você possa inspecionar a mensagem.
except, e o Python executa o primeiro que corresponder. Para tratar vários da mesma forma, liste-os em uma tupla: except (ValueError, ZeroDivisionError). A parte as e te entrega o objeto do erro para que você possa imprimir sua mensagem. else e finally
else executa apenas se nenhuma exceção ocorreu. finally sempre executa, tenha havido exceção ou não. finally é útil para limpeza que precisa acontecer independentemente do que aconteça.
try:
with open("data.txt") as f:
content = f.read()
except FileNotFoundError:
print("File not found, using defaults")
content = ""
else:
print("File loaded successfully")
finally:
print("Done attempting to load file") # sempre executafinally é mais útil para limpeza (fechando conexões, liberando bloqueios), mesmo quando você já está usando with para arquivos.
else executa apenas quando o try não levantou nada, um lugar organizado para o restante do caminho de sucesso. finally executa sempre, com ou sem exceção, o que faz dele o lugar certo para a limpeza que precisa acontecer independentemente do resultado. Com arquivos, o with já cobre essa limpeza, então você vai usar o finally menos do que imagina. raise
Você pode levantar exceções você mesmo com raise. É assim que você faz suas funções sinalizarem problemas claramente para quem as chama, em vez de retornar silenciosamente um valor errado.
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / bIsso torna suas funções explícitas sobre o que esperam e sinaliza problemas claramente para quem as chama.
raise ExceptionType("message") deixa seu próprio código sinalizar um problema em voz alta em vez de seguir adiante com um valor ruim. É assim que uma função diz "não consigo trabalhar com isso" para que quem a chamou lide com a situação. Claro e cedo é melhor do que uma resposta errada que aparece muito depois. Classes de exceção personalizadas
Para programas maiores, você pode definir seus próprios tipos de exceção herdando de Exception. Isso permite que quem chama capture seus erros específicos separadamente de outros tipos de erro.
class InsufficientFundsError(Exception):
pass
class BankAccount:
def __init__(self, balance):
self.balance = balance
def withdraw(self, amount):
if amount > self.balance:
raise InsufficientFundsError(
f"Cannot withdraw {amount}, balance is {self.balance}"
)
self.balance -= amounttry:
account.withdraw(1000)
except InsufficientFundsError as e:
print(f"Transaction declined: {e}")Exception, e um único corpo de classe vazio já é suficiente para começar. Isso dá a quem chama um nome próprio seu para capturar, separado dos erros embutidos, então os problemas do seu programa ficam por conta própria. JSON
JSON é o formato que todo mundo entende: APIs, arquivos de configuração, exportações de dados. O módulo json do Python trata isso diretamente. json.load() lê JSON de um arquivo em um dicionário ou lista do Python. json.dump() escreve um dicionário ou lista de volta em um arquivo como JSON.
Ler JSON de um arquivo:
import json
with open("config.json", "r") as f:
config = json.load(f) # converte 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 a saída legível para 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 tocar 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.
json.load(f) transforma JSON de um arquivo em dicts e lists do Python, json.dump(data, f) escreve de volta. As versões com s, json.loads e json.dumps, fazem o mesmo para uma string em vez de um arquivo. Adicione indent=2 quando quiser a saída legível. Na prática
Um padrão de salvar/carregar para um jogo pequeno: escrever o estado em JSON, carregá-lo de volta na próxima execução, e recorrer a valores padrão se ainda não existir um 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("Game saved.")
def load_game() -> dict:
try:
with open(SAVE_FILE, "r") as f:
return json.load(f)
except FileNotFoundError:
print("No save file found, starting fresh.")
return {"name": "Player", "score": 0, "level": 1}
state = load_game()
state["score"] += 50
save_game(state)
