É comum termos uma lista toda bagunçada e querermos ordenar os elementos contidos nela. Para ordenar uma lista de valores, basta chamar o método sort da lista.
Vamos ver como isso funciona na prática. Primeiramente, vamos criar uma lista com 10 elementos e depois bagunçá-la usando a função shuffle, do módulo random.
>>> lista = range(10) >>> lista [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> import random >>> random.shuffle(lista) >>> lista [2, 5, 4, 1, 3, 6, 9, 7, 0, 8]
Tudo que precisamos fazer para ordenar uma lista desordenada é:
>>> lista.sort() [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Barbada! Também podemos ordená-la de forma descendente:
>>> lista.sort(reverse=True) [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
A ordenação de inteiros ou de valores de tipos de dados simples é bem trivial. Porém, se quisermos ordenar uma lista que contém instâncias de uma classe, precisaremos utilizar o parâmetro key do sort.
O parâmetro key
O parâmetro key do método sort espera uma função que será chamada uma vez para cada um dos elementos da lista e o retorno dessa função será utilizado na comparação com os outros elementos da lista.
Considere que temos uma classe Aluno, conforme o código abaixo:
class Aluno: def __init__(self, nome, matricula): self.nome = nome self.matricula = matricula def __str__(self): return "%s - %s" % (str(self.nome), str(self.matricula))
Dada uma lista chamada alunos contendo n objetos do tipo Aluno, como ordená-la? Se chamarmos alunos.sort(), sem especificar como queremos que ela seja ordenada, o sort irá ordená-la através de comparações dos endereços de memória dos objetos contidos na lista alunos. Se quisermos que a ordenação se dê por algum dos atributos da classe, devemos especificar isso através do parâmetro key.
Vamos primeiramente criar uma lista com objetos de conteúdo aleatório:
>>> alunos = [Aluno("".join(random.sample(string.ascii_letters, 5)), random.randint(0, 100)) for i in range(10)]
>>> for aluno in alunos:
print aluno
zfbnu - 12 sxbIX - 77 vJCIN - 33 aBjZA - 70 fNLeS - 19
Bonitos os nomes deles, né? Agora, vamos ordená-los:
>>> alunos.sort(key=lambda a: a.nome)
>>> for aluno in alunos:
print aluno
aBjZA - 70 fNLeS - 19 sxbIX - 77 vJCIN - 33 zfbnu - 12
O que fizemos foi especificar como queremos que os elementos sejam comparados. Para isso, criamos uma função anônima que recebe como parâmetro um objeto e retorna o elemento a ser usado na comparação (o atributo nome). O mesmo poderia ser feito com uma função nomeada, como:
>>> def key_func(aluno): ... return aluno.nome >>> alunos.sort(key=key_func) >>> for aluno in alunos: ... print aluno
Porém, ter que criar uma função (anônima ou não) somente para indicar qual atributo deverá ser usado na ordenação é um pouco inconveniente. Por isso, vamos utilizar o outro mecanismo que permite que façamos a mesma coisa. Ao invés de criarmos uma função que recebe um objeto e retorna o atributo nome daquele objeto, vamos usar a função attrgetter do módulo operator, que retorna o valor do atributo solicitado no objeto em questão.
>>> from operator import attrgetter
>>> alunos.sort(key=attrgetter("nome"))
A função attrgetter irá retornar uma outra função que quando chamada sobre cada objeto x contido na lista alunos, irá retornar x.nome.
Ou seja, para cada objeto Aluno contido na lista, será chamado o método attrgetter, solicitando o atributo nome.
Ordenando uma lista de listas
Já vi muito código que utiliza lista ou tupla como mecanismo para agrupar dados. Ao invés de criar uma classe ou uma namedtuple, o cara vai lá e empacota os dados que deseja em uma tupla. Por exemplo, ao invés de criar uma classe Aluno, poderíamos ter empacotado os dados referentes a cada aluno em uma tupla. Veja:
>>> alunos = [("Jose", 12345), ("Maria", 28374), ("Joao", 11119), ("Joana", 12346)]
Para ordenar uma lista desse tipo, podemos continuar usando o método sort e o parâmetro key, e agora vamos especificar qual elemento das tuplas que compõem a lista será utilizado na comparação para definir qual elemento precede qual na ordem. No exemplo abaixo, estamos ordenando os alunos pelo número da matrícula.
>>> alunos.sort(key=lambda x: x[1])
>>> print alunos
[('Joao', 11119), ('Jose', 12345), ('Joana', 12346), ('Maria', 28374)]
A função anônima poderia ser evitada novamente usando a função itemgetter:
>>> from operator import itemgetter >>> alunos.sort(key=itemgetter(1))
O itemgetter é bem parecido com o attrgetter, com a diferença de que passamos para ele o índice do elemento que queremos que seja usado na comparação que será feita ao ordenar a lista.
Mas fique atento, o método sort está presente somente nas listas. Para ordenar outros objetos iteráveis, dê uma olhada na função builtin sorted.