Rafael Henrique da Silva Correia Pythonista, Linuxista, Gamer, Programador e bebedor de Cerveja
Published

março 16, 2016

←Home

Resolução exercício: 02 - Média de alunos - Grupo de Estudos Python

Dia 07/03 postei um exercício para resolução, fazer um cálculo de média ponderada de notas de alunos, o post contendo o enunciado pode ser visto aqui.

O código lixo que eu fiz no Hangout

Lixo é um nome muito forte, vamos chamar de RUIM somente por favor. Quando brincamos de live coding as coisas todas são diferentes do seu mundo real, devido a pressão, ou alguma coisa que passa na sua cabeça.

No live coding que fiz no Hangout tentei focar em muitas técnicas para manipulação de dicionários e listas, espero que isso tenha ficado claro no vídeo da aula 04 disponível neste post.

Então vamos ao código que mencionei:

# -*- coding: utf-8 -*-

bimestre_final = 6

notas = {
    "aluno1": [],
    "aluno2": [],
}

notas = {}
alunos = ["Alexsandro", "Arieh"]
bimestres = ["{} Bim".format(numero) for numero in range(1, 7)]

# esse é o for sem a list comprehenssion ai de cima
#
# bimestres = []
# for numero in range(1, 7):
#    bimestres.append("{} Bim".format(numero))
#

pesos = {
    "1 Bim": 2.0,
    "2 Bim": 3.0,
    "3 Bim": 3.0,
    "4 Bim": 4.0,
    "5 Bim": 5.0,
    "6 Bim": 5.0,
}

for aluno in alunos:
    print("Aluno: ", aluno)
    notas[aluno] = {}
    for bimestre in bimestres:
        nota = input("Digite a nota do {}: ".format(bimestre))
        nota = float(nota)
        notas[aluno][bimestre] = nota

# (7*2 + 5*3 + 3*3 + 10*4 + 9*5 + 8*5) /
# (2 + 3 + 3 + 4 + 5 + 5) = 7.40
numerador = {}
denominador = {}
for aluno in alunos:
    numerador[aluno] = 0
    denominador[aluno] = 0
    for bimestre, nota in notas[aluno].items():
        peso = pesos[bimestre]
        numerador[aluno] += nota * peso
        denominador[aluno] += peso

resultado_final = {}
for aluno in alunos:
    resultado_final[aluno] = numerador[aluno] / denominador[aluno]

# Nota maior que 9 teve menção A
# Nota maior que 7 e menor ou igual a 9 teve menção B
# Nota maior que 5 e menor ou igual a 7 teve menção C
# Nota menor que 5 teve menção D

for nome, nota in resultado_final.items():
    print("Nota final {}: {}".format(nome, nota))

    if nota > 9:
        print("Nota A")
    elif 9 >= nota > 7:
        print("Nota B")
    elif 7 >= nota > 5:
        print("Nota C")
    else:
        print("Nota D")

Vamos analisar os problemas deste código acima:

  1. Tem muitos fors ai galera, isso vai ficar lento, bem lento mesmo;
  2. As exceptions não estão tratadas de forma legal, mas você poderá estudar mais sobre isso lendo este post, considere isso um exercício;
  3. O código não está pythônico, limpo e claro, tem muita coisa que faz muita coisa.

Refatorando as variáveis e dicionários

Começando do início:

# -*- coding: utf-8 -*-

bimestre_final = 6

notas = {
    "aluno1": [],
    "aluno2": [],
}

... linhas omitidas ...

Este dicionário de notas não é muito necessário, pois quando eu recebo a nota do aluno eu já posso ir calculando durante a execução do primeiro for, isso é legal pois vai fazer com que a gente ganhe velocidade.

A variável bimestre_final é inútil também, pois como tenho uma lista com todos os bimestres abaixo deste trecho eu sei o número de bimestres que eu tenho a partir do dicionário intitulado como bimestres.

Só ai já eliminei duas coisas inúteis no script. Continuando:

... linhas omitidas ...

bimestres = ["{} Bim".format(numero) for numero in range(1, 7)]

... linhas omitidas ...

O que acham deste list comprehenssion? Este cara está mais ou menos bacana. Existem outras formas mais simples de escrever este cara, justamente porque são somente 6 bimestres. Soluções aceitáveis:

  • Primeira forma: Mais legível
... linhas omitidas ...

bimestres = [
    "1 Bim",
    "2 Bim",
    "3 Bim",
    "4 Bim",
    "5 Bim",
    "6 Bim",
]

... linhas omitidas ...
  • Segunda forma: Mais "chata"
... linhas omitidas ...

bimestres = []
for numero in range(1, 7):
   bimestres.append("{} Bim".format(numero))

... linhas omitidas ...
  • Terceira forma: "Splitada"
... linhas omitidas ...

bimestres = "1-Bim 2-Bim 3-Bim 4-Bim 5-Bim 6-Bim".split()

... linhas omitidas ...

Eu particularmente opto pela Primeira forma ou pela Terceira forma como estou optando por legibilidade e sem muita complexidade para este post vou escolher a primeira.

A Terceira forma achei legal mostrar, pois o bacana disso é que o split (que pode ser usado com strings) irá dividir a string onde tem os espaços, transformando assim essa string comprida em uma lista, que praticamente daria o mesmo resultado também (exceto pelo sinal do hífen).

O caso do dicionário pesos achei bacana, não vou mexer, mas aceito sugestões, se acharem uma solução mais legal me contem por favor.

Depois dessa primeira refatorada, comecei a criar um novo script, que ficou com o começo deste jeito:

# -*- coding: utf-8 -*-

alunos = [
    "Rafael1",
    "Rafael2"
]
bimestres = [
    "1 Bim",
    "2 Bim",
    "3 Bim",
    "4 Bim",
    "5 Bim",
    "6 Bim",
]
pesos = {
    "1 Bim": 2.0,
    "2 Bim": 3.0,
    "3 Bim": 3.0,
    "4 Bim": 4.0,
    "5 Bim": 5.0,
    "6 Bim": 5.0,
}
numerador = {}
denominador = sum(pesos.values())

Eu particularmente achei bem mais bonito e claro de entender as coisas. Só tem uma coisa diferente ai, que a é função sum que já é padrão do Python e faz soma de valores de um iterador, vamos ver como isso funciona:

$ python
Python 3.5.1 (default, Feb 22 2016, 23:22:06)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> pesos = {
...         "1 Bim": 2.0,
...         "2 Bim": 3.0,
...         "3 Bim": 3.0,
...         "4 Bim": 4.0,
...         "5 Bim": 5.0,
...         "6 Bim": 5.0,
...     }
>>> pesos.values()
dict_values([5.0, 2.0, 4.0, 5.0, 3.0, 3.0])
>>> type(pesos.values())
<class 'dict_values'>
>>> sum(pesos.values())
22.0
>>> lista = [1, 2, 3, 4, 5, 6]
>>> sum(lista)
21
>>> sum(range(1,100))
4950
>>> pesos.keys()
dict_keys(['5 Bim', '1 Bim', '4 Bim', '6 Bim', '3 Bim', '2 Bim'])
>>> sum(pesos.keys())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Olhe que curioso esse teste acima, quando uso pesos.values() pego todos os valores do dicionário pesos em forma de iterável (assunto pra outra hora o que é um iterável), ai quando uso sum somo todos esses valores e o resultado é 22.

Consigo somar também listas, e também consigo somar um range de números gerados a partir da função range. Porém quando tento somar strings (que são minhas keys do dicionário pesos) isso me retorna um erro dizendo que não consigo somar int e strings, essa soma não é possível. Espero que com esse exemplo tenha ficado claro o uso da função sum. Mais informações na documentação do Python.

Refatorando os loops

Depois de organizar melhor as variáveis/dicionários e listas que vamos trabalhar fica mais fácil organizar os nossos loops, vamos ver como ficou o script completo:

# -*- coding: utf-8 -*-

alunos = [
    "Rafael1",
    "Rafael2"
]
bimestres = [
    "1 Bim",
    "2 Bim",
    "3 Bim",
    "4 Bim",
    "5 Bim",
    "6 Bim",
]
pesos = {
    "1 Bim": 2.0,
    "2 Bim": 3.0,
    "3 Bim": 3.0,
    "4 Bim": 4.0,
    "5 Bim": 5.0,
    "6 Bim": 5.0,
}
numerador = {}
denominador = sum(pesos.values())

for aluno in alunos:
    print("Aluno: ", aluno)
    numerador[aluno] = 0
    for bimestre in bimestres:
        nota = input("Digite a nota do {}: ".format(bimestre))
        nota = float(nota)
        numerador[aluno] += nota * pesos[bimestre]

    media = numerador[aluno] / denominador
    print("Nota final {}: {:.2f}".format(aluno, media))

    if media > 9:
        print("Nota A")
    elif 9 >= media > 7:
        print("Nota B")
    elif 7 >= media > 5:
        print("Nota C")
    else:
        print("Nota D")

    print("*"*20)

Agora sim! Ficou um script clean, mais pythônico do que era antes. De 6 loops reduzi para 2 loops somente.

A criação de um dicionário dinamicamente ainda é usada na linha numerador[aluno] = 0 porém de forma bem mais simples, somente é inicializado com 0 para poder ir somando as notas do aluno no numerador da nossa equação. Para entender como ficou a estrutura do nosso dicionário numerador veja o exemplo abaixo:

$ python media_alunos_dict.py
Aluno:  Rafael1
Digite a nota do 1 Bim: 7
Digite a nota do 2 Bim: 5
Digite a nota do 3 Bim: 3
Digite a nota do 4 Bim: 10
Digite a nota do 5 Bim: 9
Digite a nota do 6 Bim: 8
> /home/rafael/Dropbox/Grupo de estudos Python/nivel_mediano/media_ponderada/media_alunos_dict.py(35)<module>()
     34     import ipdb; ipdb.set_trace()
---> 35     media = numerador[aluno] / denominador
     36     print("Nota final {}: {:.2f}".format(aluno, media))

ipdb> numerador
{'Rafael1': 163.0}

O numerador terá uma chave para cada nome de aluno contendo a soma total realizada. Neste exemplo foi realizado o seguinte cálculo no numerador do aluno Rafael1: (7*2 + 5*3 + 3*3 + 10*4 + 9*5 + 8*5) = 163.0

Quando continuo a execução do script temos:

ipdb> numerador
{'Rafael1': 163.0, 'Rafael2': 158.0}

Neste caso cálculo realizado para o aluno Rafael2 é: (5*2 + 4*3 + 6*3 + 7*4 + 8*5 + 10*5) = 158.0

Conclusão

Espero que ao final dessa solução refatorada do live coding que rolou na aula 04, você tenha entendido mais coisas sobre listas, dicionários e estruturas de repetição. Caso tenha alguma dúvida/sugestão/crítica comenta ai.

That's all folks!

Go Top
comments powered by Disqus