Módulo 4 - Funções como objetos

Prática: ordenar e transformar

12 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Ordenar uma lista de dicionários por diferentes campos com key=lambda.
  • Desempatar a ordenação combinando campos numa tupla.
  • Construir uma fábrica de funções multiplicadoras com closure.
  • Combinar ordenação e transformação num pequeno programa completo.

Ordenar uma lista de dicionários

Vamos partir de um problema concreto. Você tem uma lista de alunos, cada um um dicionário com nome e nota, e precisa apresentá-los ordenados de jeitos diferentes: por nome, por nota crescente e por nota decrescente. Tudo isso sai do parâmetro key que você viu na aula 3. A função de key recebe cada aluno e devolve o valor pelo qual comparar. Para o nome, devolve o nome; para a nota, devolve a nota. O reverse=True vira a chave para decrescente. Repare que a lista original permanece intacta, porque estamos usando sorted.

alunos = [
    {"nome": "Bruno", "nota": 7.5},
    {"nome": "Ana", "nota": 9.0},
    {"nome": "Carla", "nota": 7.5},
    {"nome": "Davi", "nota": 6.0},
]

# Por nome (alfabetico)
por_nome = sorted(alunos, key=lambda a: a["nome"])
print([a["nome"] for a in por_nome])
# ['Ana', 'Bruno', 'Carla', 'Davi']

# Por nota, do maior para o menor
por_nota = sorted(alunos, key=lambda a: a["nota"], reverse=True)
print([(a["nome"], a["nota"]) for a in por_nota])
# [('Ana', 9.0), ('Bruno', 7.5), ('Carla', 7.5), ('Davi', 6.0)]

Um mesmo dado, três ordens diferentes, cada uma numa linha com key.

O caso interessante aparece no empate. Bruno e Carla têm a mesma nota, 7,5. Qual vem primeiro? Depende do critério de desempate. Se você quer a maior nota primeiro e, entre notas iguais, ordem alfabética do nome, precisa combinar dois critérios. A tupla resolve: devolva (-a['nota'], a['nome']). O sinal de menos na nota faz a comparação numérica ir do maior para o menor, enquanto o nome, sem sinal, fica em ordem alfabética crescente. Assim você mistura decrescente e crescente na mesma ordenação, algo que reverse=True sozinho não faria.

alunos = [
    {"nome": "Bruno", "nota": 7.5},
    {"nome": "Ana", "nota": 9.0},
    {"nome": "Carla", "nota": 7.5},
    {"nome": "Davi", "nota": 6.0},
]

# Maior nota primeiro; no empate, nome em ordem alfabetica
ranking = sorted(alunos, key=lambda a: (-a["nota"], a["nome"]))
for pos, a in enumerate(ranking, start=1):
    print(f"{pos}. {a['nome']} - {a['nota']}")
# 1. Ana - 9.0
# 2. Bruno - 7.5
# 3. Carla - 7.5
# 4. Davi - 6.0

A tupla (-nota, nome) mistura decrescente e crescente no mesmo critério.

Uma fábrica de multiplicadores

A segunda parte da prática usa o closure da aula 5. Vamos escrever uma fábrica que produz funções multiplicadoras: você diz por quanto quer multiplicar e recebe uma função pronta que faz isso. Depois, montamos uma pequena coleção de multiplicadores e aplicamos cada um a um número, para ver que cada função lembra o seu próprio fator. Esse é um exemplo enxuto do padrão que sustenta ferramentas maiores, como os decoradores que virão. Note como a fábrica evita repetir código: uma definição gera quantas versões você quiser.

def criar_multiplicador(fator):
    def multiplicar(numero):
        return numero * fator
    return multiplicar

# Gera varios multiplicadores sob medida
dobro = criar_multiplicador(2)
triplo = criar_multiplicador(3)
decuplo = criar_multiplicador(10)

print(dobro(5))     # 10
print(triplo(5))    # 15
print(decuplo(5))   # 50

# Uma tabela de multiplicacao com uma fabrica
tabela = [criar_multiplicador(n) for n in range(1, 5)]
print([m(6) for m in tabela])   # [6, 12, 18, 24]

Uma fábrica, vários multiplicadores independentes, cada um com o seu fator.

Agora o momento de juntar as duas metades do módulo num programa só. Suponha que você queira aplicar um reajuste percentual a preços e depois apresentar os produtos do mais caro para o mais barato. Uma fábrica de closures cria a função de reajuste; um map ou uma compreensão aplica o reajuste; e um sorted com key ordena o resultado. Repare que escolhemos a compreensão de lista para transformar, como discutido na aula 4, porque ela lê melhor que um map com lambda. É o módulo inteiro trabalhando junto.

def criar_reajuste(percentual):
    fator = 1 + percentual / 100
    def aplicar(preco):
        return round(preco * fator, 2)
    return aplicar

produtos = [
    {"nome": "Caderno", "preco": 12.0},
    {"nome": "Caneta", "preco": 3.5},
    {"nome": "Mochila", "preco": 89.9},
]

reajustar = criar_reajuste(10)  # closure lembra 10%

# Transforma (compreensao) e ordena (sorted com key)
atualizados = [
    {"nome": p["nome"], "preco": reajustar(p["preco"])}
    for p in produtos
]
do_maior = sorted(atualizados, key=lambda p: p["preco"], reverse=True)

for p in do_maior:
    print(f"{p['nome']}: R$ {p['preco']}")
# Mochila: R$ 98.89
# Caderno: R$ 13.2
# Caneta: R$ 3.85

Fábrica com closure, compreensão para transformar e sorted com key: o módulo reunido.

Fixando: você domina o módulo

Se você acompanhou os exemplos rodando no Playground, deu um salto real. Ordenar uma lista de dicionários por qualquer critério, com desempate por tupla, é uma habilidade que aparece o tempo todo em programas de verdade, de relatórios a rankings. E construir uma fábrica de funções com closure abre a porta para os decoradores, um dos temas mais poderosos do curso. Tudo isso repousa sobre a ideia de abertura do módulo: no Python, uma função é um valor, que você guarda, passa e devolve. Com isso firme, o próximo módulo fica natural.

Teste rápido

Para ordenar alunos pela maior nota e, no empate, pelo nome em ordem alfabética, o que passar em key?

Perguntas frequentes

Por que negar a nota no key em vez de usar reverse=True?
Porque reverse=True inverte a ordenação inteira, incluindo o critério de desempate. Se você quer a nota decrescente mas o nome crescente, negar só a nota no key resolve: (-a['nota'], a['nome']). Isso mistura sentidos diferentes numa mesma ordenação, o que reverse sozinho não faz.
A ordenação altera a lista original de dicionários?
Com sorted, não: ele devolve uma lista nova e preserva a original. Se você usar o método sort na lista, ela é reordenada no lugar. Nos exemplos usamos sorted justamente para manter os dados de origem intactos e poder ordenar de vários jeitos.
Por que a prática usa compreensão para transformar e não map?
Porque a compreensão lê melhor nesse caso, como discutimos na aula 4. Ela junta a extração dos campos e a aplicação do reajuste numa expressão clara. map com lambda faria o mesmo, mas ficaria menos legível. A regra é escolher o que deixa o trecho mais claro.
O que o round faz no exemplo de reajuste?
round(valor, 2) arredonda o número para duas casas decimais, evitando resultados com muitas casas depois da multiplicação. Em valores monetários reais, o ideal é a classe Decimal para precisão, mas round já deixa a saída limpa para este exercício didático.
Como filtrar antes de ordenar, para mostrar só parte dos itens?
Aplique uma compreensão com if antes do sorted: caros = [p for p in produtos if p['preco'] > 20]. Depois ordene caros com sorted e key. Filtrar, transformar e ordenar em etapas deixa cada passo claro e fácil de conferir.
Consigo rodar toda a prática no Playground do curso?
Sim. Todo o código desta aula usa apenas a linguagem e funções embutidas, sem instalar nada. Cole cada bloco no Playground, execute e experimente mudar os dados, os critérios de ordenação e os fatores da fábrica para ver o efeito na hora.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.