Python Ordenando Listas

A professora quer a chamada em ordem alfabética. A Ana quer ver as notas da maior para a menor. O caixa quer os preços do mais barato ao mais caro. Pôr uma lista em ordem (em inglês, sort, "ordenar") é uma das coisas mais úteis que o Python faz, e ele faz em uma linha. Mas há detalhes que pegam todo mundo: a diferença entre sort() e sorted(), as maiúsculas que vêm antes das minúsculas e os nomes com acento que vão parar no fim da lista.

Você vai aprender
  • A ordenar a própria lista com sort(), e a criar uma lista nova ordenada com sorted().
  • A ordem decrescente com reverse=True, e a inverter uma lista com reverse(), reversed() e [::-1].
  • Como o Python compara textos, e por que "Zé" vem antes de "ana" e o "Álvaro" vai para o fim.
  • A escolher o critério com key: key=str.lower, key=len, key=int.
  • Os erros reais: lista = lista.sort() (a lista vira None) e a lista com números e textos misturados.

A ideia: arrumar as fichas, ou tirar uma cópia arrumada

A professora tem as fichas dos alunos numa caixa, fora de ordem. Ela pode fazer duas coisas:

  • Arrumar as fichas na própria caixa. Depois disso, a ordem antiga se perdeu. É o lista.sort().
  • Deixar a caixa como está e escrever uma folha nova, com os nomes em ordem. A caixa original continua igual. É o sorted(lista).

Os dois põem em ordem do mesmo jeito; a diferença é o que acontece com a lista original.


1. sort(): ordena a própria lista

notas = [7.0, 9.5, 6.5, 10.0, 8.0]
notas.sort()
print(notas)
notas.sort(reverse=True)
print(notas)
$ python3 sort.py
[6.5, 7.0, 8.0, 9.5, 10.0]
[10.0, 9.5, 8.0, 7.0, 6.5]
notas.sort()
O método sort ("ordenar") põe os itens da lista notas em ordem crescente, do menor para o maior. Ele muda a própria lista e, como o append, devolve None: a linha não tem =.
reverse=True
"Ao contrário = verdadeiro": a ordem fica decrescente, do maior para o menor. É um argumento nomeado: você escreve o nome (reverse), o = e o valor (aula Valores Padrão e Argumentos Nomeados).

Missão: o programa mostra as notas da menor para a maior. Faça-o mostrar da maior para a menor, usando reverse=True.

notas = [7.0, 9.5, 6.5, 10.0, 8.0]
notas.sort()
print(notas)
notas = [7.0, 9.5, 6.5, 10.0, 8.0]
notas.sort(reverse=True)
print(notas)

2. sorted(): cria uma lista nova, ordenada

turma = ["Davi", "Ana", "Carla", "Bruno"]
em_ordem = sorted(turma)
print("Ordenada:", em_ordem)
print("Original:", turma)
print("Ao contrário:", sorted(turma, reverse=True))
$ python3 sorted.py
Ordenada: ['Ana', 'Bruno', 'Carla', 'Davi']
Original: ['Davi', 'Ana', 'Carla', 'Bruno']
Ao contrário: ['Davi', 'Carla', 'Bruno', 'Ana']
sorted(turma)
Uma função (não um método: vem antes, com a lista entre parênteses). Ela devolve uma lista nova, em ordem, e não toca na original.
em_ordem = …
Aqui o = está certo: o sorted devolve a lista nova, e você a guarda.
Original
A turma continua na ordem em que foi escrita.
sorted(turma, reverse=True)
O reverse=True funciona igual no sorted.

O sorted também ordena coisas que não são listas (um texto, uma tupla, as chaves de um dicionário) e sempre devolve uma lista. O sort() só existe nas listas.

lista.sort()sorted(lista)
O que éMétodo da listaFunção
A lista originalMuda (a ordem antiga se perde)Fica como estava
DevolveNoneUma lista nova, ordenada
Como escrevernotas.sort(), sozinho na linhaem_ordem = sorted(notas) ou dentro de um print, for…
Use quandoVocê não precisa mais da ordem antigaVocê quer as duas ordens, ou só vai mostrar em ordem

3. Como o Python compara textos

Com números, "em ordem" é óbvio. Com textos, o Python compara letra por letra, da esquerda para a direita, como no dicionário. Só que ele não usa o alfabeto que você aprendeu na escola: cada caractere tem um número (o código Unicode), e a ordem é a desses números. A função ord() mostra o número de um caractere:

>>> ord("A")
65
>>> ord("Z")
90
>>> ord("a")
97
>>> ord("Á")
193
>>> "Zé" < "ana"
True
>>> "Álvaro" < "Bruno"
False

Todas as maiúsculas sem acento (65 a 90) vêm antes de todas as minúsculas (97 a 122), e as letras com acento vêm depois de todas elas (o Á é o 193). Por isso "Zé" é "menor" que "ana", e "Álvaro" é "maior" que "Bruno". Duas consequências que você precisa conhecer:

Maiúsculas antes das minúsculas

nomes = ["bruno", "Davi", "ana", "Carla"]
print(sorted(nomes))
print(sorted(nomes, key=str.lower))
$ python3 maiusculas.py
['Carla', 'Davi', 'ana', 'bruno']
['ana', 'bruno', 'Carla', 'Davi']

Na primeira linha, os nomes com maiúscula vieram antes, e os com minúscula depois: não é a ordem que uma pessoa espera. A segunda linha resolve com o key, que você vai ver na seção 4: key=str.lower quer dizer "compare como se tudo estivesse em minúsculas". Repare que os nomes continuam escritos como estavam; só a comparação ignora as maiúsculas.

O Álvaro vai para o fim

nomes = ["Bruno", "Álvaro", "Ana", "Carla", "Érica", "Eduardo"]
print(sorted(nomes))
$ python3 acentos.py
['Ana', 'Bruno', 'Carla', 'Eduardo', 'Álvaro', 'Érica']

O Álvaro, que deveria ser o primeiro da chamada, foi para depois do Eduardo, junto com a Érica. Não é um erro do Python: é a regra dos números dos caracteres. O Á (193) vem depois de qualquer letra sem acento.

Para ordenar como no dicionário: locale

O Python tem um jeito de comparar textos pelas regras de um idioma: o módulo locale (aula Módulos e import) e a função locale.strxfrm, usada como key. No terminal da Ana, com o português do Brasil instalado:

import locale

locale.setlocale(locale.LC_COLLATE, "pt_BR.UTF-8")
nomes = ["Bruno", "Álvaro", "Ana", "Carla", "Érica", "Eduardo"]
print(sorted(nomes, key=locale.strxfrm))
$ python3 acentos_locale.py
['Álvaro', 'Ana', 'Bruno', 'Carla', 'Eduardo', 'Érica']

Agora o Álvaro abre a chamada. Mas esse resultado depende dos idiomas instalados no computador: se o pt_BR.UTF-8 não estiver instalado, o setlocale para o programa com locale.Error: unsupported locale setting ("configuração de idioma não suportada"). E no editor do guia, que roda no navegador, o mesmo programa roda sem erro, mas sai na ordem do sorted comum, com o Álvaro no fim. Por isso, neste guia, fica só como nota.


4. Escolhendo o critério: key

O key ("chave") diz ao Python: "antes de comparar, passe cada item por esta função, e compare o resultado". Os itens em si não mudam; só a régua usada para comparar.

key=str.lower
Compara pela versão em minúsculas de cada texto: "Davi" é comparado como "davi".
key=len
Compara pelo tamanho de cada item.
key=int
Compara pelo número que o texto representa.

Repare que depois do key= vai o nome da função, sem parênteses: key=len, e não key=len(). Você não está chamando a função; está dizendo ao sort qual função ele deve chamar, item por item.

Pelo tamanho

compras = ["café", "arroz", "pão", "feijão", "sal"]
print(sorted(compras, key=len))
$ python3 tamanho.py
['pão', 'sal', 'café', 'arroz', 'feijão']

Do item mais curto (3 letras) para o mais longo (6). O pão e o sal têm o mesmo tamanho, e ficaram na ordem em que já estavam na lista: quando dois itens "empatam", o Python mantém a ordem original entre eles.

Números guardados como texto

As senhas da fila do banco chegaram como texto. Ordenadas assim, o resultado é estranho:

senhas = ["10", "9", "2", "100"]
print(sorted(senhas))
print(sorted(senhas, key=int))
$ python3 texto_numero.py
['10', '100', '2', '9']
['2', '9', '10', '100']

Como textos, eles são comparados caractere por caractere: "10" começa com "1", que vem antes de "2", então "10" e "100" ficam antes do "2". Com key=int, cada texto é comparado como o número que representa, e a ordem fica certa. (Os itens continuam sendo textos: repare nas aspas.)

Listas dentro de listas

Quando os itens são listas, o Python compara o primeiro item de cada uma; se empatar, o segundo, e assim por diante:

>>> turma = [["Davi", 7.0], ["Ana", 9.5], ["Carla", 8.0]]
>>> sorted(turma)
[['Ana', 9.5], ['Carla', 8.0], ['Davi', 7.0]]

Ficou em ordem de nome. Para ordenar pela nota (o segundo item), o key precisa de uma função que pegue o item 1 de cada par. Você vai aprender a escrever essas funções nas aulas Funções e lambda.

Missão: alguns nomes foram digitados em minúsculas e a chamada saiu fora de ordem. Use o key para ordenar sem ligar para maiúsculas.

chamada = ["carla", "Davi", "ana", "Bruno"]
print(sorted(chamada))
chamada = ["carla", "Davi", "ana", "Bruno"]
print(sorted(chamada, key=str.lower))

5. Invertendo a ordem (sem ordenar)

Às vezes você não quer ordenar: quer só a lista de trás para frente, como ela está. Há três jeitos:

fila = ["Ana", "Bruno", "Carla"]
fila.reverse()
print(fila)
fila = ["Ana", "Bruno", "Carla"]
print(fila[::-1])
print(list(reversed(fila)))
for nome in reversed(fila):
    print(nome)
print(fila)
$ python3 inverter.py
['Carla', 'Bruno', 'Ana']
['Carla', 'Bruno', 'Ana']
['Carla', 'Bruno', 'Ana']
Carla
Bruno
Ana
['Ana', 'Bruno', 'Carla']
fila.reverse()
Método: inverte a própria lista e devolve None, como o sort().
fila[::-1]
Uma fatia com passo -1 (aula Acessando Itens): uma lista nova, invertida.
reversed(fila)
Uma função que entrega os itens de trás para frente, sem criar uma lista. É perfeita no for. Para ter uma lista, ponha num list().
print(fila)
A última linha prova: [::-1] e reversed não mudaram a fila.

Se você mostrar o reversed(fila) direto, não aparece uma lista, e sim um objeto que entrega os itens quando alguém pede (o número no fim é um endereço da memória, e muda a cada execução):

>>> fila = ["Ana", "Bruno", "Carla"]
>>> reversed(fila)
<list_reverseiterator object at 0x7f78566ef070>
reverse() não é "ordem decrescente"

notas.reverse() só vira a lista de cabeça para baixo: [7, 10, 8] vira [8, 10, 7], que não está em ordem nenhuma. Para a ordem decrescente, é notas.sort(reverse=True).


Formas diferentes de ordenar: qual usar

Você querEscrevaA lista original
Ordenar a lista e esquecer a ordem antigalista.sort()Muda
Uma cópia ordenadanova = sorted(lista)Fica igual
Do maior para o menorlista.sort(reverse=True) ou sorted(lista, reverse=True)Muda / fica igual
Textos sem ligar para maiúsculassorted(lista, key=str.lower)Fica igual
Pelo tamanhosorted(lista, key=len)Fica igual
Números que estão em textosorted(lista, key=int)Fica igual
Só inverter a ordem atuallista.reverse(), lista[::-1] ou reversed(lista)Muda / igual / igual
Mostrar em ordem, uma vezfor x in sorted(lista):Fica igual

Quando dá errado

lista = lista.sort(): a lista vira None

O mesmo erro do append, com outro método:

compras = ["leite", "arroz", "feijão", "café"]
compras = compras.sort()
print(compras)
print(compras[0])
$ python3 sort_none.py
None
Traceback (most recent call last):
  File "/home/ana/sort_none.py", line 4, in <module>
    print(compras[0])
          ~~~~~~~^^^
TypeError: 'NoneType' object is not subscriptable

Linha por linha:

  • Linha 2: o sort() ordenou a lista e devolveu None; o = guardou esse None em compras. A lista ordenada ficou sem nome e se perdeu.
  • Linha 3: o print mostrou None.
  • Linha 4: TypeError: 'NoneType' object is not subscriptable, "o objeto NoneType não aceita colchetes". Subscriptable é "que aceita índice". O None não tem itens, então compras[0] não faz sentido.

Você pode ver o None direto:

>>> fila = ["Ana", "Bruno", "Carla"]
>>> print(fila.sort())
None

Como consertar: ou compras.sort() sozinho na linha, ou compras = sorted(compras). Nunca os dois misturados. O reverse() tem o mesmo comportamento.

Números e textos misturados
coisas = [3, "2", 1]
coisas.sort()
print(coisas)
$ python3 mistura.py
Traceback (most recent call last):
  File "/home/ana/mistura.py", line 2, in <module>
    coisas.sort()
    ~~~~~~~~~~~^^
TypeError: '<' not supported between instances of 'str' and 'int'

"O < não funciona entre um str e um int". Para ordenar, o Python precisa perguntar "este é menor que aquele?" (com o <), e ele não sabe dizer se o texto "2" é menor que o número 3. Quase sempre, isso acontece quando alguns números vieram do input() como texto. Como consertar: converta tudo para o mesmo tipo antes (int(), aula Conversão de Tipos), ou ordene com key=int se todos forem números escritos como texto.

A ordem "errada" que não é erro

Três surpresas que não dão mensagem nenhuma, só um resultado esquisito:

  • Minúsculas depois das maiúsculas: ['Carla', 'Davi', 'ana', 'bruno']. Use key=str.lower.
  • Acentos no fim: o Álvaro depois do Eduardo. É a ordem dos códigos dos caracteres; veja a nota sobre locale na seção 3.
  • Números em texto: ['10', '100', '2', '9']. Use key=int, ou guarde os números como números.

Referência rápida

EscritaO que fazDevolve
lista.sort()Ordena a própria lista, do menor para o maiorNone
lista.sort(reverse=True)…do maior para o menorNone
lista.sort(key=função)…comparando função(item)None
sorted(lista)Cria uma lista nova ordenada (aceita reverse e key)A lista nova
lista.reverse()Inverte a própria listaNone
reversed(lista)Entrega os itens de trás para frente (para o for ou o list())Um iterador
lista[::-1]Cria uma lista nova invertidaA lista nova
key=str.lower / key=len / key=intIgnorar maiúsculas / pelo tamanho / pelo número do texto—
SintomaCausa provável
A lista virou Nonelista = lista.sort() ou lista = lista.reverse()
TypeError: 'NoneType' object is not subscriptableA variável recebeu None antes (quase sempre pelo motivo acima)
TypeError: '<' not supported between instances of 'str' and 'int'Lista com números e textos misturados
Minúsculas depois das maiúsculasA ordem é a dos códigos: use key=str.lower
Nomes com acento no fimA ordem é a dos códigos (Á = 193)
'10' antes de '2'Números guardados como texto: key=int
A lista "ao contrário" não está em ordemUsou reverse() em vez de sort(reverse=True)

Exercícios

1. Do maior para o menor

Complete para ordenar os preços do mais caro para o mais barato:

precos.sort()

2. Uma cópia em ordem

Complete com a função que devolve uma lista nova, ordenada, sem mudar turma:

chamada = (turma)

3. Sem ligar para maiúsculas

Complete o critério:

sorted(nomes, key=)

4. Depois de a = [3, 1, 2] e b = sorted(a), quanto vale a?

5. O que mostra print(sorted(["bia", "Ana", "Caio"]))?

6. Qual linha deixa notas igual a None?

Missão final: a lista em ordem

Missão: a Ana quer a lista de compras em ordem alfabética e o primeiro item, mas o programa mostra None e quebra. Conserte a linha do sort.

compras = ["leite", "arroz", "feijão", "café"]
compras = compras.sort()
print(compras)
print(f"Primeiro: {compras[0]}")
compras = ["leite", "arroz", "feijão", "café"]
compras.sort()
print(compras)
print(f"Primeiro: {compras[0]}")

Resumo

  • lista.sort() ordena a própria lista e devolve None; sorted(lista) devolve uma lista nova e não mexe na original.
  • reverse=True dá a ordem decrescente, nos dois.
  • key=função escolhe o critério: str.lower (ignorar maiúsculas), len (tamanho), int (números em texto). Sem parênteses depois do nome.
  • Textos são comparados pelos códigos dos caracteres: maiúsculas antes das minúsculas, e letras com acento depois de todas (o Álvaro vai para o fim).
  • reverse(), reversed() e [::-1] só invertem a ordem atual; não ordenam.
  • Nunca lista = lista.sort(). E não dá para ordenar números misturados com textos.