Artigo original: https://www.freecodecamp.org/news/the-python-sort-list-array-method-ascending-and-descending-explained-with-examples/

Se quiser aprender a trabalhar com o método sort() em seus projetos em Python, este artigo é para você. Esse método é bastante poderoso e você pode personalizá-lo para que atenda às suas necessidades, então vamos ver como ele funciona em detalhes.

Você vai aprender:

  • Como usar esse método e como personalizar suas funcionalidades.
  • Quando usá-lo e quando não o usar.
  • Como chamá-lo passando diferentes combinações de argumentos.
  • Como ordenar uma lista em ordem ascendente e descendente.
  • Como comparar os elementos de uma lista com base em valores intermediários.
  • Como passar funções lambda para esse método.
  • Como esse método pode ser comparado à função sorted().
  • Por que o método sort() realiza uma ordenação estável.
  • Como funciona o processo de mutação internamente.

Prontos? Vamos começar! ⭐

🔹 Finalidade e casos de uso

Com o método sort(), você pode ordenar uma lista em:

  • Ordem ascendente
  • Ordem descendente

Esse método é usado para ordenar uma lista diretamente, o que significa que ela realiza a mutação da lista ou que a modifica diretamente sem criar cópias adicionais. Por isso, lembre-se:

image-113
O método sort() tem a ver com mutação

Você aprenderá mais sobre mutação neste artigo (prometo!), mas, por agora, é importante que você saiba que o método sort() modifica a lista e que, portanto, sua versão original se perde.

Por causa disso, você somente deve usar esse método se:

  • Quiser modificar (ordenar) a lista permanentemente.
  • Não precisar manter a versão original da lista.

Se essas duas característica forem atendidas, então o método .sort() é exatamente o que você estava procurando.

🔸 Sintaxe e argumentos

Vejamos como chamar o método .sort() para tirar proveito de todo o seu potencial.

Abaixo vemos a chamada mais básica (sem argumentos):

image-41
Caixa laranja: "A lista que você deseja ordenar" - Caixa verde: chamada do método

Se você não passar argumentos, por padrão:

  • A lista será ordenada em ordem ascendente.
  • Os elementos da lista serão comparados diretamente usando seus valores com o operador <.

Por exemplo:

>>> b = [6, 3, 8, 2, 7, 3, 9]
>>> b.sort()
>>> b
[2, 3, 3, 6, 7, 8, 9] # Ordenada!

Argumentos personalizados

Para personalizar o modo como o método sort() funciona, você pode passar dois argumentos opcionais:

  • Key (em português, chave)
  • Reverse (em português, inverter)

Vejamos como eles alteram o comportamento desse método. Aqui temos uma chamada ao método com esses dois argumentos:

image-42
Caixa verde: "Dois argumentos possíveis"

Antes de explicar como eles funcionam, eu gostaria de explicar algo que você provavelmente já notou na imagem acima – na chamada ao método, os nomes dos parâmetros têm de ser incluídos antes de seus valores correspondentes, assim:

  • key=<f> (em português, chave=<f>)
  • reverse=<value> (em português, inverter=<valor>)

Isso ocorre porque eles são argumentos somente nomeados. Se estiver passando um valor personalizado para eles, os nomes precisam ser especificados na chamada ao método, seguidos por um sinal de igual = e seus valores correspondentes, assim:

image-46
Caixa azul: "Eles precisam ser incluídos" (os nomes dos argumentos)

Do contrário, se você tentar passar os argumentos diretamente como normalmente fazemos com parâmetros posicionais, verá a mensagem de erro abaixo, pois a função não saberá qual argumento corresponde a qual parâmetro:

TypeError: sort() takes no positional arguments

Reverse

Agora que sabemos o que é um argumento somente nomeado, comecemos com reverse (em português, inverter).

Os valores de reverse podem ser True ou False:

  • False significa que a lista será ordenada em ordem ascendente.
  • True significa que a lista será ordenada em ordem descendente (inversa).

💡 Dica: Por padrão, o valor é False – se você não passar argumentos para esse parâmetro, a lista será ordenada na ordem ascendente.

Aqui temos alguns exemplos:

image-123
Por padrão, reverse é False
# Lista de números inteiros
>>> b = [6, 3, 8, 2, 7, 3, 9]
>>> b.sort()
>>> b
[2, 3, 3, 6, 7, 8, 9]

# Lista de strings
>>> c = ["A", "Z", "D", "T", "U"]
>>> c.sort()
>>> c
['A', 'D', 'T', 'U', 'Z']

💡 Dica: se os elementos da lista forem strings, eles serão ordenados alfabeticamente.

image-117
Aqui, especificamos que reverse é True, então a lista tem de ser ordenada em ordem descendente (inversa).
# Lista de números inteiros
>>> b = [6, 3, 8, 2, 7, 3, 9]
>>> b.sort(reverse=True)
>>> b
[9, 8, 7, 6, 3, 3, 2]

# Lista de strings
>>> c = ["A", "Z", "D", "T", "U"]
>>> c.sort(reverse=True)
>>> c
['Z', 'U', 'T', 'D', 'A']

💡 Dica: observe como agora a lista está ordenada na ordem descendente, pois reverse é True.

Key

Agora que você sabe como trabalhar com o parâmetro reverse, vejamos o parâmetro key (em português, chave).

Esse parâmetro é um pouco mais detalhado, pois ele determina como os elementos da lista são comparados durante o processo de ordenação.

image-120
Sintaxe básica: Caixa laranja - "A lista que você quer ordenar" | Caixa verde - "Chamada do método" | Caixa azul - "Argumento somente nomeado" | Caixa lilás - "Função (somente o nome)"

O valor de key pode ser:

  • None, o que significa que os elementos da lista serão comparados diretamente. Por exemplo, em uma lista de inteiros, os próprios inteiros serão usados para a comparação.
  • Uma função de um argumento que gera um valor intermediário para cada elemento. Esse valor intermediário é calculado apenas uma vez e é usado para fazer comparações durante todo o processo de ordenação. Usamos isso quando não queremos comparar os elementos diretamente, por exemplo, quando queremos comparar strings com base em seu tamanho (o valor intermediário).

💡 Dica: por padrão, o valor de key é None, então os elementos são comparados diretamente.

Por exemplo:

Digamos que nossa intenção é ordenar uma lista de strings com base no tamanho de cada uma delas, da string menor para a string maior. Podemos passar a função len como o valor de key, assim:

>>> d = ["aaa", "bb", "c"]
>>> d.sort(key=len)
>>> d
['c', 'bb', 'aaa']

💡 Dica: observe que estamos apenas passando o nome da função (len) sem parênteses, pois não estamos chamando a função. Isso é muito importante.

Observe a diferença entre comparar os elementos diretamente e comparar seu tamanho (veja abaixo). Usando o valor padrão de key (None), a ordenação das strings seria alfabética (à esquerda), mas agora estamos ordenando-as com base em seu tamanho (à direita):

image-49

O que está acontecendo internamente? Cada elemento é passado como um argumento para a função len(). O valor retornado por essa função é usado para realizar as comparações durante o processo de ordenação:

image-122-1

O resultado disso é uma lista com um critério de ordenação diferente: o tamanho da string.

Vejamos agora um outro exemplo:

Outro exemplo interessante é ordenar uma lista de strings como se todas elas fossem escritas e letras minúsculas (por exemplo, fazer com que "Aa" seja equivalente a "aa").

De acordo com a ordem lexicográfica, as letras maiúsculas vêm antes das minúsculas:

>>> "E" < "e"
True

Assim, a string "Emma" viria antes de "emily" em uma lista ordenada, mesmo que suas versões em letras minúsculas estivessem na ordem oposta:

>>> "Emma" < "emily"
True
>>> "emma" < "emily"
False

Para evitar a distinção entre letras maiúsculas e minúsculas, passamos a função str.lower como key. Isso gerará uma versão em letras minúsculas das strings, que será usada para as comparações:

>>> e = ["Emma", "emily", "Amy", "Jason"]
>>> e.sort(key=str.lower)
>>> e
['Amy', 'emily', 'Emma', 'Jason']

Observe que, agora, "emily" vem antes de "Emma" em uma lista ordenada, que é exatamente o que queremos.

💡 Dica: se tivéssemos usado o processo de ordenação padrão, todas as strings que começassem com uma letra maiúscula viriam antes de todas as strings que começassem com letra minúscula:

>>> e = ["Emma", "emily", "Amy", "Jason"]
>>> e.sort()
>>> e
['Amy', 'Emma', 'Jason', 'emily']

Aqui temos um exemplo usando a Programação Orientada a Objetos (POO):

Se temos essa classe simples em Python:

>>> class Client:
	def __init__(self, age):
		self.age = age

E se criarmos quatro instâncias:

>>> client1 = Client(67)
>>> client2 = Client(23)
>>> client3 = Client(13)
>>> client4 = Client(35)

Podemos criar uma lista que referencie essas quatro instâncias:

>>> clients = [client1, client2, client3, client4]

Em seguida, se definirmos uma função para obter age (em português, idade) nessas instâncias:

>>> def get_age(client):
	return client.age

podemos ordenar a lista com base na idade de cada cliente passando a função get_age como argumento:

>>> clients.sort(key=get_age)

Essa é a lista final em sua versão ordenada. Usamos um laço for para imprimir a idade das instâncias na ordem em que elas aparecem na lista:

>>> for client in clients:
	print(client.age)

	
13
23
35
67

Exatamente o que queríamos – agora, a lista está ordenada por ordem ascendente de idade das instâncias.

💡 Dica: em vez de definir uma função get_age, poderíamos ter usado uma função lambda para obter a idade de cada instância, assim:

>>> clients.sort(key=lambda x: x.age)

As funções lambda são funções anônimas (ou seja, sem um nome), pequenas e simples. Elas são muito úteis nesses cenários em que somente queremos usá-las em locais específicos e por pouco tempo.

Esta é a estrutura básica da função lambda que estamos usando para ordenar a lista:

image-90
Estrutura básica de uma função lambda | Caixa laranja: "palavra-chave usada para definir uma função lambda (como 'def')" | Caixa verde: "Parâmetro" | Caixa azul: "Valor de retorno"

Passando os dois argumentos

Ótimo! Agora, você já sabe como personalizar a funcionalidade do método sort(). Se quiser, no entanto, levar sua habilidade a um outro patamar, combine o efeito de key e de reverse na mesma chamada ao método:

>>> f = ["A", "a", "B", "b", "C", "c"]
>>> f.sort(key=str.lower, reverse=True)
>>> f
['C', 'c', 'B', 'b', 'A', 'a']
Ordenar a lista na ordem inversa como se as strings estivessem em letras minúsculas.

Aqui temos as combinações de argumentos diferentes e seus efeitos:

image-124
key=None e reverse=False: Compara os elementos com base no seu valor e ordene-os em ordem ascendente. | key=None e reverse=True: Compara os elementos com base no seu valor e ordene-os em ordem descendente. | key=<Função> e reverse=False: Compara os elementos com base nos valores retornados pela função e ordene-os em ordem ascendente. | key=<Função> e reverse=True: Compara os elementos com base nos valores retornados pela função e ordene-os em ordem descendente.

A ordem dos argumentos somente nomeados não importa

Como estamos especificando o nome dos argumentos, já sabemos quais valores correspondem a quais parâmetros, por isso podemos incluir key ou reverse primeiro na lista e o efeito será exatamente o mesmo.

Assim, esta chamada ao método:

image-53

É equivalente a:

image-54

Aqui temos um exemplo:

>>> a = ["Zz", "c", "y", "o", "F"]
>>> a.sort(key=str.lower, reverse=True)
>>> a
['Zz', 'y', 'o', 'F', 'c']

Se mudarmos a ordem dos argumentos, teremos exatamente o mesmo resultado:

>>> a = ["Zz", "c", "y", "o", "F"]
>>> a.sort(reverse=True, key=str.lower)
>>> a
['Zz', 'y', 'o', 'F', 'c']

🔹 Valor de retorno

Agora, vamos falar um pouco sobre o valor de retorno desse método. O método sort() retorna None – ele não retorna uma versão ordenada da lista, como poderíamos esperar intuitivamente.

De acordo com a documentação do Python:

Para lembrar aos usuários que ele opera por efeito colateral, ele não retorna a sequência ordenada.

Basicamente, esse valor de retorno é usado para nos lembrar de que estamos modificando a lista original na memória, em vez de gerar uma cópia da lista.

Esse é um exemplo do valor de retorno de sort():

>>> nums = [6.5, 2.4, 7.3, 3.5, 2.6, 7.4]

# Atribui o valor de retorno a essa variável:
>>> val = nums.sort()

# Verifica o valor de retorno:
>>> print(val)
None

Viu? None foi retornado pela chamada ao método.

💡 Dica: é importante não confundir o método sort() com a função sorted(), que é uma função que opera de modo semelhante, mas que não modifica a lista original. Em vez disso, sorted() gera e retorna uma nova cópia da lista, já ordenada.

Aqui temos um exemplo que podemos usar para comparar as duas funções:

# O método sort() retorna None
>>> nums = [6.5, 2.4, 7.3, 3.5, 2.6, 7.4]
>>> val = nums.sort()
>>> print(val)
None
Exemplo de .sort()
# sorted() retorna uma cópia nova e ordenada da lista original
>>> nums = [6.5, 2.4, 7.3, 3.5, 2.6, 7.4]
>>> val = sorted(nums)
>>> val
[2.4, 2.6, 3.5, 6.5, 7.3, 7.4]

# Ela, no entanto, não modifica a lista original
>>> nums
[6.5, 2.4, 7.3, 3.5, 2.6, 7.4]
Exemplo de sorted()

Isso é muito importante, pois o efeito é bem diferente. Usar o método sort() quando queríamos usar o método sorted() pode resultar em bugs sérios para o seu programa, pois você pode não perceber que a lista está sofrendo uma mutação.

🔸 O método sort() realiza uma ordenação estável

Vamos falar um pouco agora sobre as características do algoritmo de ordenação usado por sort().

Esse método realiza uma ordenação estável, pois funciona com uma implementação de TimSort, um algoritmo de ordenação bastante eficiente e estável.

De acordo com a documentação do Python:

Uma ordenação é estável se ela garante não alterar a ordem relativa dos elementos que são comparados como iguais — isso é útil para a ordenação em passadas múltiplas (por exemplo, ordenar por departamento e, depois, por faixa salarial).

Isso significa que, se dois elementos tiverem o mesmo valor ou valor intermediário (key), com certeza eles ficarão na mesma ordem relativa entre si.

Vejamos o significado disso. Observe este exemplo por um instante:

>>> d = ["BB", "AA", "CC", "A", "B", "AAA", "BBB"]
>>> d.sort(key=len)
>>> d
['A', 'B', 'BB', 'AA', 'CC', 'AAA', 'BBB']

Estamos comparando os elementos com base em seu tamanho, pois passamos a função len como o argumento para key.

Podemos ver que há três elementos com o tamanho 2: "BB", "AA" e "CC" , nessa ordem.

Agora, observe que esses três elementos estão na mesma ordem relativa na lista ordenada final:

image-92

Isso ocorre porque o algoritmo certamente será estável e os três tinham o mesmo valor intermediário (key) durante o processo de ordenação (seu tamanho era 2, então key era 2).

💡 Dica: o mesmo aconteceu com "A" e "B" (tamanho 1) e "AAA" e "BBB" (tamanho 3). A ordem relativa original dos elementos entre eles foi preservada.

Agora, você sabe como funciona o método sort(). Vamos tratar em mais detalhes sobre mutação e sobre como isso pode afetar o seu programa.

🔹 Mutação e riscos

Como prometido, veremos como esses processos de mutação funcionam internamente:

Podemos definir uma lista em Python, assim:

a = [1, 2, 3, 4]

Estamos criando um objeto em um local específico da memória. Esse local é chamado de "endereço de memória" do objeto, representado por um número inteiro exclusivo, chamado de id.

Imagine o id como se ele fosse uma "etiqueta" usada para identificar um local específico na memória:

image-40

Você pode acessar o id de uma lista usando a função id(), passando a lista como argumento:

>>> a = [1, 2, 3, 4]
>>> id(a)
60501512

Ao mutar a lista, você a altera diretamente na memória. Você pode se perguntar: "mas o que há de tão arriscado nisso?"

É arriscado, pois afeta cada linha de código que utilize a lista após a mutação. Assim, você pode estar escrevendo um código que funcione com uma lista que é completamente diferente da lista real que existe na memória após a mutação.

É por isso que você precisa ter muito cuidado com os métodos que causam mutações.

Especificamente, o método sort() faz a mutação da lista. Aqui temos um exemplo do seu efeito:

image-94

Aqui temos um exemplo:

# Define uma lista
>>> a = [7, 3, 5, 1]

# Confere o id da lista
>>> id(a)
67091624

# Ordena a lista usando .sort()
>>> a.sort()

# Confere o id da lista (é o mesmo, ou seja, a lista é o mesmo objeto na memória)
>>> id(a)
67091624

# Agora, a lista está ordenada. Ela sofreu mutação!
>>> a
[1, 3, 5, 7]

A lista foi mutada após a chamada a .sort().

Cada linha de código que funcione com uma lista a após a mutação ter ocorrido usará a versão nova e ordenada da lista. Se não era isso que você tinha em mente, você pode não perceber que as outras partes do seu programa estejam funcionando com a nova versão da lista.

Aqui temos outro exemplo das riscos da mutação dentro da função:

# Lista
>>> a = [7, 3, 5, 1]

# Função que imprime os elementos da lista em ordem ascendente.
>>> def print_sorted(x):
	x.sort()
	for elem in x:
		print(elem)

# Chamada à função passando 'a' como argumento	
>>> print_sorted(a)
1
3
5
7

# Opa! A lista original foi mutada.
>>> a
[1, 3, 5, 7]

A lista a que foi passada como argumento foi mutada, mesmo que essa não tenha sido sua intenção quando escreveu a função inicialmente.

💡 Dica: se uma função faz a mutação de um argumento, deve-se declarar isso com clareza para evitar introduzir bugs em outras partes do seu programa.

🔸 Resumo do método sort()

  • O método sort() permite que você organize uma lista em ordem ascendente ou descendente.
  • Ele recebe argumentos somente nomeados: key e reverse.
  • reverse determina se a lista é ordenada em ordem ascendente ou descendente.
  • key é uma função que gera um valor intermediário para cada elemento. Esse valor é usado para fazer as comparações durante o processo de ordenação.
  • O método sort() causa a mutação da lista e suas alterações são permanentes. É preciso ter muito cuidado e usar o método somente se você não necessitar mais da versão original da lista.

Espero sinceramente que tenha gostado do artigo e que o tenha achado útil. Agora, você pode trabalhar com o método sort() em seus projetos em Python.

Confira os cursos on-line da autora e siga-a no Twitter. ⭐️