Python Projeto: Notas da Turma
A professora da escola do bairro guarda as notas da turma numa planilha e, todo bimestre, faz as contas à mão: a média de cada aluno, quem passou, quem vai para a recuperação. A Ana se ofereceu para automatizar isso. A planilha é exportada como CSV (um arquivo de texto com os valores separados por vírgulas, aula Arquivos CSV), e o programa vai ler esse arquivo, calcular tudo, mostrar um relatório arrumado e gravar o resultado num CSV novo, pronto para abrir na planilha. Quatro versões, uma novidade por vez.
- open e with e o módulo csv (
DictReadereDictWriter). - Dicionários e listas, com float() para transformar texto em número.
- Funções com return, e if e else para a situação.
- f-strings com alinhamento (Formatação) para o relatório.
- sort com lambda para ordenar pela média, e list comprehension para contar os aprovados.
O que vamos construir
A pasta do projeto tem o arquivo de notas que a professora exportou:
nome,turma,nota1,nota2,nota3 Bia,A,8.0,9.5,7.0 Caio,B,6.0,5.5,5.0 Duda,A,9.0,10.0,9.5 Enzo,B,7.0,7.0,7.0 Gabi,A,4.5,6.0,5.5 Hugo,B,10.0,8.5,9.0
A primeira linha é o cabeçalho: o nome de cada coluna. Cada linha seguinte é um aluno, com a turma e as três notas do bimestre. A regra da escola: média 7 ou mais, aprovado; abaixo de 7, recuperação. Esta é a versão final rodando no computador da Ana:
$ python3 notas.py Aluno Turma Média Situação ------------------------------------ Duda A 9.50 aprovado Hugo B 9.17 aprovado Bia A 8.17 aprovado Enzo B 7.00 aprovado Caio B 5.50 recuperação Gabi A 5.33 recuperação ------------------------------------ Média da turma: 7.44 Aprovados: 4 de 6 Gravei resultado.csv $ cat resultado.csv nome,turma,media,situacao Duda,A,9.50,aprovado Hugo,B,9.17,aprovado Bia,A,8.17,aprovado Enzo,B,7.00,aprovado Caio,B,5.50,recuperação Gabi,A,5.33,recuperação
(O cat mostra o conteúdo de um arquivo de texto no terminal.) As quatro versões:
- Ler o CSV com
csv.DictReader. - Média e situação, em funções.
- O relatório, alinhado e ordenado pela média.
- Gravar o resultado.csv com
csv.DictWriter.
Antes de começar
Crie a pasta do projeto, entre nela e salve lá o notas.csv de cima (num editor de texto, ou exportando da planilha como "CSV separado por vírgulas"). O programa vai se chamar notas.py, na mesma pasta:
$ mkdir -p projetos/notas $ cd projetos/notas
As caixas Experimente deste projeto têm duas abas: o programa (main.py) e o notas.csv, que fica na mesma pasta do programa. Você pode mexer nas notas da aba do CSV e rodar de novo para ver o resultado mudar.
Versão 1 · Ler o CSV
import csv
with open("notas.csv", newline="", encoding="utf-8") as arquivo:
alunos = list(csv.DictReader(arquivo))
print(f"{len(alunos)} alunos lidos")
print(alunos[0])
for aluno in alunos:
print(aluno["nome"], aluno["turma"])
- import csv
- O módulo da biblioteca padrão que entende arquivos CSV: separa as colunas, lida com aspas e vírgulas dentro dos valores.
- open("notas.csv", newline="", encoding="utf-8")
- Abre o arquivo para leitura. O
newline=""é a recomendação da documentação para arquivos CSV (o módulocsvcuida das quebras de linha sozinho), e oencoding="utf-8"garante que os acentos sejam lidos certo. A aula Arquivos CSV explica os dois. - csv.DictReader(arquivo)
- Lê cada linha como um dicionário, usando o cabeçalho como chaves. O cabeçalho em si não vira aluno.
- list(…)
- Junta todos os dicionários numa lista, para usar depois que o
withfechar o arquivo. - aluno["nome"]
- Cada valor se pega pelo nome da coluna, e não pela posição. Se a professora mudar a ordem das colunas, o programa continua certo.
$ python3 notas.py
6 alunos lidos
{'nome': 'Bia', 'turma': 'A', 'nota1': '8.0', 'nota2': '9.5', 'nota3': '7.0'}
Bia A
Caio B
Duda A
Enzo B
Gabi A
Hugo B
Olhe bem o dicionário da Bia: 'nota1': '8.0', com aspas. Tudo o que vem de um CSV é texto, inclusive as notas. Para fazer conta, a versão 2 vai precisar converter com float(). Esquecer isso é o erro mais comum deste projeto (veja Quando dá errado).
Missão: em vez de só o nome, faça o programa mostrar uma frase por aluno, como Caio está na turma B, usando as colunas nome e turma.
import csv
with open("notas.csv", newline="", encoding="utf-8") as arquivo:
alunos = list(csv.DictReader(arquivo))
for aluno in alunos:
print(aluno["nome"])
nome,turma,nota1,nota2,nota3 Bia,A,8.0,9.5,7.0 Caio,B,6.0,5.5,5.0 Duda,A,9.0,10.0,9.5 Enzo,B,7.0,7.0,7.0 Gabi,A,4.5,6.0,5.5 Hugo,B,10.0,8.5,9.0
import csv
with open("notas.csv", newline="", encoding="utf-8") as arquivo:
alunos = list(csv.DictReader(arquivo))
for aluno in alunos:
print(f"{aluno['nome']} está na turma {aluno['turma']}")
Versão 2 · Média e situação
Agora as contas. Cada tarefa vira uma função com um nome claro: uma lê o arquivo e já converte as notas, outra calcula a média, outra decide a situação.
import csv
def ler_alunos(caminho):
alunos = []
with open(caminho, newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
alunos.append({"nome": linha["nome"], "turma": linha["turma"], "notas": notas})
return alunos
def media(notas):
return sum(notas) / len(notas)
def situacao(m):
if m >= 7:
return "aprovado"
return "recuperação"
for aluno in ler_alunos("notas.csv"):
m = media(aluno["notas"])
print(f"{aluno['nome']}: {m:.2f} {situacao(m)}")
- def ler_alunos(caminho):
- Lê o CSV e devolve uma lista de alunos já "limpos": cada um é um dicionário com
nome,turmaenotas, uma lista de números. - float(linha["nota1"])
- Converte o texto
'8.0'no número8.0. A conversão é feita uma vez, na leitura; o resto do programa já recebe números. - sum(notas) / len(notas)
- A média: a soma das notas dividida pela quantidade (aula Funções Embutidas). Se um dia houver quatro notas, a conta continua certa.
- if m >= 7: return "aprovado"
- A regra da escola. Repare no
>=: com 7,00 exato, o aluno passa. - {m:.2f}
- A média com duas casas:
8.1666…aparece como8.17. O valor guardado não muda; só o jeito de mostrar.
$ python3 notas.py Bia: 8.17 aprovado Caio: 5.50 recuperação Duda: 9.50 aprovado Enzo: 7.00 aprovado « 7,00 exato: passou, por causa do >= Gabi: 5.33 recuperação Hugo: 9.17 aprovado
Missão: a função situacao ainda devolve "?" para todo mundo. Escreva a regra: "aprovado" se a média for 7 ou mais; senão, "recuperação". Atenção ao Enzo, que tem 7,00 exato: ele deve aparecer como Enzo: 7.00 aprovado.
import csv
def ler_alunos(caminho):
alunos = []
with open(caminho, newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
alunos.append({"nome": linha["nome"], "turma": linha["turma"], "notas": notas})
return alunos
def media(notas):
return sum(notas) / len(notas)
def situacao(m):
return "?" # troque: "aprovado" se m for 7 ou mais; senão, "recuperação"
for aluno in ler_alunos("notas.csv"):
m = media(aluno["notas"])
print(f"{aluno['nome']}: {m:.2f} {situacao(m)}")
nome,turma,nota1,nota2,nota3 Bia,A,8.0,9.5,7.0 Caio,B,6.0,5.5,5.0 Duda,A,9.0,10.0,9.5 Enzo,B,7.0,7.0,7.0 Gabi,A,4.5,6.0,5.5 Hugo,B,10.0,8.5,9.0
import csv
def ler_alunos(caminho):
alunos = []
with open(caminho, newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
alunos.append({"nome": linha["nome"], "turma": linha["turma"], "notas": notas})
return alunos
def media(notas):
return sum(notas) / len(notas)
def situacao(m):
if m >= 7:
return "aprovado"
return "recuperação"
for aluno in ler_alunos("notas.csv"):
m = media(aluno["notas"])
print(f"{aluno['nome']}: {m:.2f} {situacao(m)}")
Versão 3 · O relatório
A professora quer imprimir uma tabela: os alunos do melhor para o pior, com as colunas alinhadas, e um resumo da turma no fim. A média e a situação passam a ser guardadas em cada aluno, para o relatório e a ordenação usarem.
import csv
def ler_alunos(caminho):
alunos = []
with open(caminho, newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
alunos.append({"nome": linha["nome"], "turma": linha["turma"], "notas": notas})
return alunos
def media(notas):
return sum(notas) / len(notas)
def situacao(m):
if m >= 7:
return "aprovado"
return "recuperação"
def relatorio(alunos):
print(f"{'Aluno':<8}{'Turma':^7}{'Média':>7} Situação")
print("-" * 36)
for aluno in alunos:
print(f"{aluno['nome']:<8}{aluno['turma']:^7}{aluno['media']:>7.2f} {aluno['situacao']}")
print("-" * 36)
aprovados = [a for a in alunos if a["situacao"] == "aprovado"]
media_turma = sum(a["media"] for a in alunos) / len(alunos)
print(f"Média da turma: {media_turma:.2f}")
print(f"Aprovados: {len(aprovados)} de {len(alunos)}")
alunos = ler_alunos("notas.csv")
for aluno in alunos:
aluno["media"] = media(aluno["notas"])
aluno["situacao"] = situacao(aluno["media"])
alunos.sort(key=lambda a: a["media"], reverse=True)
relatorio(alunos)
As partes novas, uma por uma
- aluno["media"] = media(aluno["notas"])
- Acrescenta duas chaves em cada dicionário de aluno,
mediaesituacao, calculadas uma vez só. - alunos.sort(key=lambda a: a["media"], reverse=True)
- Ordena a lista pela média. O
keydiz o que comparar em cada aluno (umalambda, função de uma linha, aula lambda), e oreverse=Truepõe a maior primeiro (aula Ordenando Listas). - {'Aluno':<8}
- O texto
Alunoalinhado à esquerda num espaço de 8 caracteres. O que sobra vira espaço em branco. - {'Turma':^7}
- Centralizado em 7 caracteres.
- {aluno['media']:>7.2f}
- O número alinhado à direita em 7 caracteres, com duas casas. Números alinhados à direita deixam as vírgulas uma embaixo da outra.
- "-" * 36
- Repete o traço 36 vezes: a linha da tabela (aula Juntar e Repetir Textos).
- [a for a in alunos if a["situacao"] == "aprovado"]
- Uma lista só com os aprovados (aula List Comprehension); o
lendela conta quantos são. - sum(a["media"] for a in alunos)
- Soma as médias de todos os alunos, sem criar uma lista no meio.
$ python3 notas.py Aluno Turma Média Situação ------------------------------------ Duda A 9.50 aprovado Hugo B 9.17 aprovado Bia A 8.17 aprovado Enzo B 7.00 aprovado Caio B 5.50 recuperação Gabi A 5.33 recuperação ------------------------------------ Média da turma: 7.44 Aprovados: 4 de 6
As colunas ficaram alinhadas porque cada campo ocupa sempre o mesmo número de caracteres, seja qual for o nome. (Num nome com mais de 8 letras, o alinhamento quebraria: o f-string nunca corta um texto, só completa os que são menores.)
Missão: o relatório está saindo na ordem do arquivo. Na linha marcada, ordene a lista alunos da maior média para a menor, para a Duda aparecer primeiro e a Gabi por último.
import csv
def ler_alunos(caminho):
alunos = []
with open(caminho, newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
alunos.append({"nome": linha["nome"], "turma": linha["turma"], "notas": notas})
return alunos
def media(notas):
return sum(notas) / len(notas)
def situacao(m):
if m >= 7:
return "aprovado"
return "recuperação"
def relatorio(alunos):
print(f"{'Aluno':<8}{'Turma':^7}{'Média':>7} Situação")
print("-" * 36)
for aluno in alunos:
print(f"{aluno['nome']:<8}{aluno['turma']:^7}{aluno['media']:>7.2f} {aluno['situacao']}")
print("-" * 36)
aprovados = [a for a in alunos if a["situacao"] == "aprovado"]
media_turma = sum(a["media"] for a in alunos) / len(alunos)
print(f"Média da turma: {media_turma:.2f}")
print(f"Aprovados: {len(aprovados)} de {len(alunos)}")
alunos = ler_alunos("notas.csv")
for aluno in alunos:
aluno["media"] = media(aluno["notas"])
aluno["situacao"] = situacao(aluno["media"])
# ordene aqui os alunos, da maior média para a menor
relatorio(alunos)
nome,turma,nota1,nota2,nota3 Bia,A,8.0,9.5,7.0 Caio,B,6.0,5.5,5.0 Duda,A,9.0,10.0,9.5 Enzo,B,7.0,7.0,7.0 Gabi,A,4.5,6.0,5.5 Hugo,B,10.0,8.5,9.0
import csv
def ler_alunos(caminho):
alunos = []
with open(caminho, newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
alunos.append({"nome": linha["nome"], "turma": linha["turma"], "notas": notas})
return alunos
def media(notas):
return sum(notas) / len(notas)
def situacao(m):
if m >= 7:
return "aprovado"
return "recuperação"
def relatorio(alunos):
print(f"{'Aluno':<8}{'Turma':^7}{'Média':>7} Situação")
print("-" * 36)
for aluno in alunos:
print(f"{aluno['nome']:<8}{aluno['turma']:^7}{aluno['media']:>7.2f} {aluno['situacao']}")
print("-" * 36)
aprovados = [a for a in alunos if a["situacao"] == "aprovado"]
media_turma = sum(a["media"] for a in alunos) / len(alunos)
print(f"Média da turma: {media_turma:.2f}")
print(f"Aprovados: {len(aprovados)} de {len(alunos)}")
alunos = ler_alunos("notas.csv")
for aluno in alunos:
aluno["media"] = media(aluno["notas"])
aluno["situacao"] = situacao(aluno["media"])
alunos.sort(key=lambda a: a["media"], reverse=True)
relatorio(alunos)
Dá para fazer com alunos.sort(…), que ordena a própria lista, ou com alunos = sorted(alunos, …), que cria uma lista nova ordenada. Os dois aceitam key e reverse.
Versão 4 · Gravar o resultado.csv
Por fim, o resultado vai para um arquivo que a professora abre na planilha. Uma função nova, gravar_resultado, usa o csv.DictWriter, o caminho de volta do DictReader:
import csv
def ler_alunos(caminho):
alunos = []
with open(caminho, newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
alunos.append({"nome": linha["nome"], "turma": linha["turma"], "notas": notas})
return alunos
def media(notas):
return sum(notas) / len(notas)
def situacao(m):
if m >= 7:
return "aprovado"
return "recuperação"
def relatorio(alunos):
print(f"{'Aluno':<8}{'Turma':^7}{'Média':>7} Situação")
print("-" * 36)
for aluno in alunos:
print(f"{aluno['nome']:<8}{aluno['turma']:^7}{aluno['media']:>7.2f} {aluno['situacao']}")
print("-" * 36)
aprovados = [a for a in alunos if a["situacao"] == "aprovado"]
media_turma = sum(a["media"] for a in alunos) / len(alunos)
print(f"Média da turma: {media_turma:.2f}")
print(f"Aprovados: {len(aprovados)} de {len(alunos)}")
def gravar_resultado(alunos, caminho):
with open(caminho, "w", newline="", encoding="utf-8") as arquivo:
escritor = csv.DictWriter(arquivo, fieldnames=["nome", "turma", "media", "situacao"])
escritor.writeheader()
for aluno in alunos:
escritor.writerow({
"nome": aluno["nome"],
"turma": aluno["turma"],
"media": f"{aluno['media']:.2f}",
"situacao": aluno["situacao"],
})
alunos = ler_alunos("notas.csv")
for aluno in alunos:
aluno["media"] = media(aluno["notas"])
aluno["situacao"] = situacao(aluno["media"])
alunos.sort(key=lambda a: a["media"], reverse=True)
relatorio(alunos)
gravar_resultado(alunos, "resultado.csv")
print("Gravei resultado.csv")
- open(caminho, "w", newline="", encoding="utf-8")
- Abre para escrever (
"w", de write). Se o arquivo não existe, é criado; se existe, é apagado e reescrito (aula Escrevendo Arquivos). - csv.DictWriter(arquivo, fieldnames=[…])
- Um escritor que recebe dicionários. O
fieldnamesdiz quais colunas, e em que ordem. - escritor.writeheader()
- Escreve a linha do cabeçalho, com os nomes das colunas.
- escritor.writerow({…})
- Escreve uma linha. As chaves do dicionário têm de ser exatamente os
fieldnames. - f"{aluno['media']:.2f}"
- A média vai para o arquivo como texto com duas casas,
9.50, e não9.5nem9.166666666666666. - "notas" não está na lista
- O dicionário de cada aluno tem a chave
notas, mas ela não vai para o arquivo: owriterowrecebe um dicionário novo, só com as quatro colunas.
$ python3 notas.py Aluno Turma Média Situação ------------------------------------ Duda A 9.50 aprovado Hugo B 9.17 aprovado Bia A 8.17 aprovado Enzo B 7.00 aprovado Caio B 5.50 recuperação Gabi A 5.33 recuperação ------------------------------------ Média da turma: 7.44 Aprovados: 4 de 6 Gravei resultado.csv $ cat resultado.csv nome,turma,media,situacao Duda,A,9.50,aprovado Hugo,B,9.17,aprovado Bia,A,8.17,aprovado Enzo,B,7.00,aprovado Caio,B,5.50,recuperação Gabi,A,5.33,recuperação
Por padrão, o módulo csv termina cada linha com \r\n (o fim de linha do Windows), como pede a especificação dos arquivos CSV. No terminal isso não aparece, e as planilhas leem sem problema. É por isso que o newline="" é importante também na gravação: sem ele, em alguns sistemas, cada linha ganharia uma quebra a mais.
Missão: a função gravar_resultado ainda está vazia, e o programa diz "Gravei resultado.csv" sem gravar nada (mentindo!). Escreva o corpo dela com csv.DictWriter: o cabeçalho nome,turma,media,situacao e uma linha por aluno, com a média em duas casas, como Caio,B,5.50,recuperação.
import csv
def ler_alunos(caminho):
alunos = []
with open(caminho, newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
alunos.append({"nome": linha["nome"], "turma": linha["turma"], "notas": notas})
return alunos
def media(notas):
return sum(notas) / len(notas)
def situacao(m):
if m >= 7:
return "aprovado"
return "recuperação"
def relatorio(alunos):
print(f"{'Aluno':<8}{'Turma':^7}{'Média':>7} Situação")
print("-" * 36)
for aluno in alunos:
print(f"{aluno['nome']:<8}{aluno['turma']:^7}{aluno['media']:>7.2f} {aluno['situacao']}")
print("-" * 36)
aprovados = [a for a in alunos if a["situacao"] == "aprovado"]
media_turma = sum(a["media"] for a in alunos) / len(alunos)
print(f"Média da turma: {media_turma:.2f}")
print(f"Aprovados: {len(aprovados)} de {len(alunos)}")
def gravar_resultado(alunos, caminho):
pass # troque: grave nome, turma, media (com 2 casas) e situacao com csv.DictWriter
alunos = ler_alunos("notas.csv")
for aluno in alunos:
aluno["media"] = media(aluno["notas"])
aluno["situacao"] = situacao(aluno["media"])
alunos.sort(key=lambda a: a["media"], reverse=True)
relatorio(alunos)
gravar_resultado(alunos, "resultado.csv")
print("Gravei resultado.csv")
nome,turma,nota1,nota2,nota3 Bia,A,8.0,9.5,7.0 Caio,B,6.0,5.5,5.0 Duda,A,9.0,10.0,9.5 Enzo,B,7.0,7.0,7.0 Gabi,A,4.5,6.0,5.5 Hugo,B,10.0,8.5,9.0
import csv
def ler_alunos(caminho):
alunos = []
with open(caminho, newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
alunos.append({"nome": linha["nome"], "turma": linha["turma"], "notas": notas})
return alunos
def media(notas):
return sum(notas) / len(notas)
def situacao(m):
if m >= 7:
return "aprovado"
return "recuperação"
def relatorio(alunos):
print(f"{'Aluno':<8}{'Turma':^7}{'Média':>7} Situação")
print("-" * 36)
for aluno in alunos:
print(f"{aluno['nome']:<8}{aluno['turma']:^7}{aluno['media']:>7.2f} {aluno['situacao']}")
print("-" * 36)
aprovados = [a for a in alunos if a["situacao"] == "aprovado"]
media_turma = sum(a["media"] for a in alunos) / len(alunos)
print(f"Média da turma: {media_turma:.2f}")
print(f"Aprovados: {len(aprovados)} de {len(alunos)}")
def gravar_resultado(alunos, caminho):
with open(caminho, "w", newline="", encoding="utf-8") as arquivo:
escritor = csv.DictWriter(arquivo, fieldnames=["nome", "turma", "media", "situacao"])
escritor.writeheader()
for aluno in alunos:
escritor.writerow({
"nome": aluno["nome"],
"turma": aluno["turma"],
"media": f"{aluno['media']:.2f}",
"situacao": aluno["situacao"],
})
alunos = ler_alunos("notas.csv")
for aluno in alunos:
aluno["media"] = media(aluno["notas"])
aluno["situacao"] = situacao(aluno["media"])
alunos.sort(key=lambda a: a["media"], reverse=True)
relatorio(alunos)
gravar_resultado(alunos, "resultado.csv")
print("Gravei resultado.csv")
Quando dá errado
float(): unsupported operand type(s) for +: 'int' and 'str'
import csv
with open("notas.csv", newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
notas = [linha["nota1"], linha["nota2"], linha["nota3"]]
print(linha["nome"], sum(notas) / 3)
$ python3 notas.py
Traceback (most recent call last):
File "/home/ana/projetos/notas/notas.py", line 6, in <module>
print(linha["nome"], sum(notas) / 3)
~~~^^^^^^^
TypeError: unsupported operand type(s) for +: 'int' and 'str'
"Tipos de operando não suportados para +: int e str". O sum começa do número 0 e vai somando: 0 + '8.0', um número com um texto. As notas vieram do CSV como texto. Como consertar: converta cada nota com float() antes de somar, de preferência logo na leitura, como na versão 2.
KeyError
import csv
with open("notas.csv", newline="", encoding="utf-8") as arquivo:
for linha in csv.DictReader(arquivo):
print(linha["Nome"])
$ python3 notas.py
Traceback (most recent call last):
File "/home/ana/projetos/notas/notas.py", line 5, in <module>
print(linha["Nome"])
~~~~~^^^^^^^^
KeyError: 'Nome'
No cabeçalho do arquivo está nome, com minúscula, e as chaves do DictReader são exatamente o que está no cabeçalho. Como consertar: confira a primeira linha do CSV (um print(alunos[0]), como na versão 1, mostra as chaves de verdade) e use o mesmo nome. Se a planilha tiver espaços no cabeçalho (nota 1), a chave também terá.
could not convert string to float: '5,5'
A professora digitou uma nota do Caio à moda brasileira, com vírgula. No CSV, como a vírgula separa as colunas, a planilha pôs aspas em volta do valor:
nome,turma,nota1,nota2,nota3 Bia,A,8.0,9.5,7.0 Caio,B,6.0,"5,5",5.0 …
$ python3 notas.py
Traceback (most recent call last):
File "/home/ana/projetos/notas/notas.py", line 23, in <module>
for aluno in ler_alunos("notas.csv"):
~~~~~~~~~~^^^^^^^^^^^^^
File "/home/ana/projetos/notas/notas.py", line 8, in ler_alunos
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
~~~~~^^^^^^^^^^^^^^^^
ValueError: could not convert string to float: '5,5'
O csv leu certo (tirou as aspas e entregou '5,5'), mas o float() só entende ponto como separador decimal. O rastro mostra o caminho: a linha 23 chamou o ler_alunos, e a linha 8, dentro dele, falhou; os ^^^ apontam o float(linha["nota2"]), a segunda nota. Como consertar: troque a vírgula antes de converter, float(linha["nota2"].replace(",", ".")), ou exporte a planilha com ponto. (Planilhas em português costumam exportar com ponto e vírgula entre as colunas e vírgula nos decimais; a aula Arquivos CSV mostra o delimiter=";".)
could not convert string to float: ''
Se uma célula da planilha ficou vazia (Caio,B,6.0,,5.0), a mensagem é a mesma, com um texto vazio entre as aspas:
$ python3 notas.py
Traceback (most recent call last):
File "/home/ana/projetos/notas/notas.py", line 23, in <module>
for aluno in ler_alunos("notas.csv"):
~~~~~~~~~~^^^^^^^^^^^^^
File "/home/ana/projetos/notas/notas.py", line 8, in ler_alunos
notas = [float(linha["nota1"]), float(linha["nota2"]), float(linha["nota3"])]
~~~~~^^^^^^^^^^^^^^^^
ValueError: could not convert string to float: ''
'' é o texto vazio: não há número nenhum para converter. Como consertar: decida a regra com a professora (nota em branco vale zero? o aluno fica de fora?) e escreva no programa, por exemplo com um try/except ValueError (aula try e except) que avisa qual aluno está com a nota faltando.
FileNotFoundError
$ python3 projetos/notas/notas.py
Traceback (most recent call last):
File "/home/ana/projetos/notas/notas.py", line 23, in <module>
for aluno in ler_alunos("notas.csv"):
~~~~~~~~~~^^^^^^^^^^^^^
File "/home/ana/projetos/notas/notas.py", line 6, in ler_alunos
with open(caminho, newline="", encoding="utf-8") as arquivo:
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'notas.csv'
A Ana estava na pasta pessoal (~) e rodou o programa pelo caminho. O programa e o CSV estão juntos em projetos/notas, mas o open("notas.csv") procura o arquivo na pasta onde o terminal está, e não na pasta do programa. Como consertar: entre na pasta do projeto antes (cd projetos/notas) e rode python3 notas.py. (A aula pathlib mostra como achar a pasta do próprio programa, para ele funcionar de qualquer lugar.)
Formas diferentes de guardar cada aluno
Neste projeto, cada aluno é um dicionário. Seria possível usar outras estruturas:
| Forma | Exemplo | Quando usar |
|---|---|---|
| Dicionário | aluno["media"] | O que o projeto usa: casa direto com o DictReader e o DictWriter, e as chaves têm nome |
| Lista ou tupla | aluno[3] | Evite aqui: aluno[3] não diz nada a quem lê, e uma coluna nova bagunça as posições |
Classe Aluno | aluno.media | Quando o aluno passa a ter comportamento (a média virando um @property, por exemplo). Veja as Dataclasses |
Desafios extras
- Reprovado: acrescente uma terceira situação,
reprovado, para média abaixo de 5. Quem fica com a Gabi (5,33)? - Por turma: mostre a média de cada turma (A e B) e quantos aprovados cada uma teve. Um dicionário com a turma como chave ajuda (aula Percorrendo um Dicionário).
- Vírgula decimal: faça o
ler_alunosaceitar5,5e5.5, e avisar (sem quebrar) quando uma nota estiver em branco. - O nome do arquivo na linha de comando: receba o nome do CSV em
sys.argv(aula Os Módulos os e sys), para usar o mesmo programa com outras turmas.
Referência rápida do projeto
| Peça | O que faz |
|---|---|
open("notas.csv", newline="", encoding="utf-8") | Abre o CSV para ler |
list(csv.DictReader(arquivo)) | Lista de dicionários, um por linha; chaves = cabeçalho |
float(linha["nota1"]) | Texto do CSV → número |
sum(notas) / len(notas) | Média |
alunos.sort(key=lambda a: a["media"], reverse=True) | Ordena da maior média para a menor |
f"{nome:<8}{turma:^7}{media:>7.2f}" | Colunas alinhadas: esquerda, centro, direita |
open(caminho, "w", newline="", encoding="utf-8") | Abre para gravar (apaga o que havia) |
csv.DictWriter(arquivo, fieldnames=[…]) | Escritor de dicionários |
writeheader() / writerow({…}) | Grava o cabeçalho / uma linha |
Exercícios
1. O leitor de dicionários
Complete o nome da classe do módulo csv que lê cada linha como dicionário:
2. Texto em número
Complete para transformar a nota lida do CSV num número com casas decimais:
3. Alinhado à direita
Complete o formato que mostra a média alinhada à direita em 7 caracteres, com duas casas:
4. A versão 1 mostrou 'nota1': '8.0', com aspas. O que isso quer dizer?
5. Por que o Enzo, com média 7,00, aparece como aprovado?
6. O que acontece com um resultado.csv que já existia quando o programa abre o arquivo com "w"?
Resumo
- Versão 1:
csv.DictReaderlê cada linha como dicionário, com o cabeçalho como chaves. Tudo vem como texto. - Versão 2:
float()na leitura, uma função para a média e outra para a situação (>= 7, aprovado). - Versão 3:
sort(key=…, reverse=True)ordena pela média;:<,:^e:>nas f-strings alinham as colunas. - Versão 4:
csv.DictWritercomwriteheader()ewriterow()grava oresultado.csv; o"w"apaga o que havia. - Os erros típicos: nota sem
float()(TypeError), coluna com outro nome (KeyError), vírgula decimal ou nota vazia (ValueError) e o programa rodado de outra pasta (FileNotFoundError).