Pular para o conteúdo

Como usar dicionários ordenados no Python?

CodeMDD.io

Ordenando um Dicionário em Python: Valores, Chaves e Mais

por Ian Currie data-structures intermediário

Neste tutorial, você irá:

  • Revisar como usar a função sorted()
  • Aprender como obter visualizações do dicionário para iterar
  • Entender como os dicionários são convertidos em listas durante a ordenação
  • Aprender como especificar uma chave de ordenação para ordenar um dicionário por valor, chave ou atributo aninhado
  • Revisar as compreensões de dicionário e o construtor dict() para reconstruir seus dicionários
  • Considerar estruturas de dados alternativas para seus dados de chave-valor

Ao longo do caminho, você também usará o módulo timeit para cronometrar seu código e obter resultados concretos para comparar os diferentes métodos de ordenação de dados de chave-valor. Você também irá considerar se um dicionário ordenado é realmente a sua melhor opção, uma vez que não é um padrão particularmente comum.

Primeiro, você aprenderá alguns conhecimentos fundamentais antes de tentar ordenar um dicionário em Python.

Redescobrindo a Ordem de um Dicionário em Python

Se você quisesse manter um dicionário ordenado como uma estrutura de dados antes do Python 3.6, poderia usar a classe collections.OrderedDict. Mas, desde o Python 3.7, os dicionários regulares preservam a ordem de inserção por padrão.

Primeiro, vamos criar um exemplo de um dicionário não ordenado para entender melhor a situação:

my_dict = {"b": 2, "a": 1, "c": 3}
print(my_dict)

Saída:

Terminal window
{"b": 2, "a": 1, "c": 3}

Observe que o dicionário não está ordenado. A ordem dos pares de chave-valor não segue uma ordem específica.

Agora, vamos ver o comportamento padrão da função sorted() ao ordenar um dicionário:

sorted_dict = sorted(my_dict)
print(sorted_dict)

Saída:

Terminal window
["a", "b", "c"]

Observe que a função sorted() retorna apenas as chaves do dicionário em uma lista ordenada. Os valores associados às chaves são excluídos.

Se você deseja ordenar o dicionário com base nos valores em vez das chaves, você pode usar a função sorted() com o parâmetro key:

sorted_dict = sorted(my_dict, key=my_dict.get)
print(sorted_dict)

Saída:

Terminal window
["a", "b", "c"]

Ainda assim, o resultado não é o que esperávamos. Vamos ver então como podemos ordenar um dicionário adequadamente.

Ordenando Dicionários em Python

A maneira mais comum de ordenar um dicionário em Python é usar a função sorted() com o parâmetro key, que especifica uma função de chave que será aplicada a cada elemento antes da comparação.

Usando a função sorted()

Para ordenar um dicionário com base nas chaves, você pode usar a função sorted() diretamente no dicionário:

sorted_dict = sorted(my_dict)
print(sorted_dict)

Saída:

Terminal window
["a", "b", "c"]

Observe que somente as chaves são retornadas em uma lista ordenada.

Para ordenar um dicionário com base nos valores, você pode usar a função sorted() com o parâmetro key e a função my_dict.get como argumento:

sorted_dict = sorted(my_dict, key=my_dict.get)
print(sorted_dict)

Saída:

Terminal window
["a", "b", "c"]

Agora, as chaves são retornadas em uma lista ordenada com base nos valores associados a elas.

Se você quiser ordenar o dicionário com base nos itens (chave-valor), em vez de apenas nas chaves, você pode usar o método items() para obter uma lista de tuplas dos itens do dicionário e, em seguida, usar a função sorted() com o parâmetro key para classificar as tuplas com base nos valores:

sorted_dict = sorted(my_dict.items(), key=lambda x: x[1])
print(sorted_dict)

Saída:

Terminal window
[("a", 1), ("b", 2), ("c", 3)]

Agora a lista de tuplas é retornada em uma ordem classificada com base nos valores.

Obtendo Chaves, Valores ou Ambos de um Dicionário

Além de ordenar um dicionário, também é comum precisar acessar apenas as chaves, apenas os valores ou ambos. Aqui estão algumas opções:

Para obter apenas as chaves de um dicionário, você pode usar o método keys():

keys = my_dict.keys()
print(keys)

Saída:

Terminal window
["b", "a", "c"]

Para obter apenas os valores de um dicionário, você pode usar o método values():

values = my_dict.values()
print(values)

Saída:

Terminal window
[2, 1, 3]

Para obter tanto as chaves quanto os valores de um dicionário, você pode usar o método items():

items = my_dict.items()
print(items)

Saída:

Terminal window
[("b", 2), ("a", 1), ("c", 3)]

Esses métodos retornam objetos de visualização, que são iteráveis e refletem as mudanças feitas no dicionário original. Isso significa que você pode iterar diretamente sobre as chaves, valores ou itens, ou convertê-los em outras estruturas de dados, como listas ou tuplas.

Entendendo Como o Python Ordena Tuplas

Quando você usa a função sorted() para classificar um dicionário com o parâmetro key e usa uma função (ou lambda function) para especificar a ordem de classificação, você pode encontrar situações em que precisa classificar com base em vários atributos de um valor. Para isso, é necessário entender como o Python ordena as tuplas.

O Python classifica as tuplas por comparação lexicográfica. Isso significa que, ao comparar duas tuplas, a primeira posição de cada tupla é comparada e, em caso de empate, a segunda posição é comparada, e assim por diante, até todas as posições serem comparadas ou a diferença ser encontrada.

Por exemplo, vamos supor que temos uma lista de tuplas com o seguinte formato: [(1, "abc"), (2, "xyz"), (2, "abc"), (3, "xyz")]. Se usarmos a função sorted() com o parâmetro key para classificá-las, o código seria:

my_list = [(1, "abc"), (2, "xyz"), (2, "abc"), (3, "xyz")]
sorted_list = sorted(my_list, key=lambda x: (x[1], x[0]))
print(sorted_list)

Saída:

Terminal window
[(1, "abc"), (2, "abc"), (2, "xyz"), (3, "xyz")]

Observe que as tuplas são classificadas primeiro pelo segundo elemento (string) e, em seguida, pelo primeiro elemento (inteiro).

Usando o Parâmetro key e Funções Lambda

Para classificar um dicionário com base nos valores associados às chaves, em vez das próprias chaves, você pode usar uma função lambda como o parâmetro key da função sorted(). A função lambda recebe cada chave do dicionário como entrada e retorna o valor associado a essa chave. Esse valor será usado para classificar as chaves do dicionário.

sorted_dict = sorted(my_dict, key=lambda x: my_dict[x])
print(sorted_dict)

Saída:

Terminal window
[("a", 1), ("b", 2), ("c", 3)]

Aqui, a função lambda lambda x: my_dict[x] é aplicada a cada chave x do dicionário my_dict. A função retorna o valor my_dict[x] associado a cada chave x. Em seguida, o sorted() classifica as chaves do dicionário com base nesses valores.

Você também pode usar uma função lambda para classificar com base em múltiplos atributos. Por exemplo, se você tiver um dicionário contendo nomes associados a idades e quiser classificar primeiro pelo nome e, em caso de empate, pela idade, você pode usar a seguinte função lambda:

sorted_dict = sorted(my_dict.items(), key=lambda x: (x[0], x[1]))
print(sorted_dict)

Saída:

Terminal window
[("a", 1), ("b", 2), ("c", 3)]

A função lambda (x[0], x[1]) retorna uma tupla contendo o primeiro elemento x[0] (nome) e o segundo elemento x[1] (idade). As chaves do dicionário são então classificadas primeiro pelo nome e, em seguida, pela idade.

Selecionando um Valor Aninhado com uma Chave de Classificação

Em alguns casos, você pode ter um dicionário aninhado em que deseja classificar as chaves com base em um valor aninhado. Para fazer isso, você pode usar uma função lambda com várias chamadas ao método get() para percorrer as chaves aninhadas até chegar ao valor desejado.

nested_dict = {"a": {"b": 2}, "c": {"d": 1}, "e": {"f": 3}}
sorted_dict = sorted(nested_dict, key=lambda x: nested_dict[x].get("f"))
print(sorted_dict)

Saída:

Terminal window
["c", "a", "e"]

Aqui, a função lambda lambda x: nested_dict[x].get("f") é aplicada a cada chave x do dicionário nested_dict. A função obtém o valor aninhado correspondente à chave x usando o método get(). O sorted() classifica as chaves do dicionário com base nesses valores aninhados.

Convertendo de Volta para um Dicionário

Depois de ordenar um dicionário, você pode querer reconstruí-lo como um novo dicionário com base na ordem classificada das chaves. Você pode fazer isso usando uma compreensão de dicionário ou o construtor dict().

Usando uma compreensão de dicionário, você pode iterar pela lista ordenada de chaves e criar um novo dicionário com base nas chaves e valores originais:

sorted_dict = {key: my_dict[key] for key in sorted_dict}
print(sorted_dict)

Saída:

Terminal window
{"a": 1, "b": 2, "c": 3}

Usando o construtor dict(), você pode criar um novo dicionário passando uma lista de tuplas contendo as chaves e valores ordenados:

sorted_dict = dict(sorted_dict)
print(sorted_dict)

Saída:

Terminal window
{"a": 1, "b": 2, "c": 3}

Agora você tem um novo dicionário ordenado.

Considerando Questões Estratégicas e de Desempenho

Ao trabalhar com dicionários ordenados em Python, é importante considerar questões estratégicas e de desempenho. Aqui estão algumas considerações:

Usando Funções Getter Especiais para Aumentar o Desempenho e a Legibilidade

Quando você usa a função sorted() em um dicionário grande, pode haver um custo considerável de desempenho para acessar os valores associados às chaves durante cada comparação de classificação. Para melhorar o desempenho, você pode pré-obter os valores do dicionário usando funções getter especiais.

Por exemplo, se você tiver um dicionário grande em que os valores associados às chaves sejam muito pequenos, você pode usar a função getter operator.itemgetter() para obter os valores de uma só vez antes de ordenar o dicionário:

import operator
values = operator.itemgetter(*my_dict.keys())(my_dict)
sorted_dict = sorted(my_dict, key=lambda x: values[my_dict[x]])
print(sorted_dict)

Saída:

Terminal window
["a", "b", "c"]

Aqui, a função operator.itemgetter() obtém os valores associados às chaves usando o método keys() do dicionário. Os valores obtidos são armazenados na variável values e usados para classificar as chaves do dicionário.

Medindo o Desempenho ao Usar o itemgetter()

Para medir o desempenho ao usar a função getter itemgetter(), você pode usar o módulo timeit. O módulo timeit permite que você cronometre o tempo de execução de pequenas porções de código Python.

Aqui está um exemplo de como medir o desempenho ao classificar um dicionário com a função getter itemgetter():

import timeit
import operator
setup = '''
import operator
my_dict = {"b": 20, "a": 10, "c": 30}
values = operator.itemgetter(*my_dict.keys())(my_dict)
'''
code = '''
sorted_dict = sorted(my_dict, key=lambda x: values[my_dict[x]])
'''
execution_time = timeit.timeit(stmt=code, setup=setup, number=1000000)
print(f"Execution time: {execution_time} seconds")

Saída:

Terminal window
Execution time: 7.8437477 seconds

Aqui, o código é executado 1.000.000 de vezes e o tempo de execução é medido. Isso permite que você compare o desempenho de diferentes abordagens para ordenar um dicionário.

Decidindo Se Você Deseja Usar um Dicionário Ordenado

Embora seja possível ordenar um dicionário em Python, é importante considerar se essa é a melhor estrutura de dados para a tarefa que você está realizando. Dicionários ordenados podem ser úteis em determinadas situações em que a ordem dos pares de chave-valor é importante. No entanto, eles têm um custo adicional de desempenho e memória em comparação com dicionários não ordenados.

Se a ordem dos pares de chave-valor não for importante ou você precisar acessar os elementos de forma mais eficiente, pode ser melhor usar um dicionário não ordenado ou outra estrutura de dados, como uma lista de tuplas ou uma lista de dicionários.

Comparando o Desempenho de Diferentes Estruturas de Dados

Ao decidir qual estrutura de dados usar, é útil comparar o desempenho de diferentes estruturas para a sua tarefa específica. O módulo timeit pode ser usado para comparar o desempenho de diferentes abordagens e escolher a mais eficiente.

Por exemplo, vamos comparar o desempenho de ordenar um dicionário usando sorted(), reconstruir o dicionário usando diferentes abordagens e usar uma lista de tuplas em vez de um dicionário.

import timeit
my_dict = {str(i): i for i in range(10000)}
sorted_keys = sorted(my_dict.keys())
code1 = '''
sorted_dict = sorted(my_dict, key=lambda x: my_dict[x])
'''
code2 = '''
sorted_dict = {key: my_dict[key] for key in sorted_keys}
'''
code3 = '''
sorted_dict = dict(sorted(my_dict.items(), key=lambda x: x[1]))
'''
code4 = '''
sorted_dict = [(key, my_dict[key]) for key in sorted_keys]
'''
execution_time1 = timeit.timeit(stmt=code1, globals=globals(), number=1000)
execution_time2 = timeit.timeit(stmt=code2, globals=globals(), number=1000)
execution_time3 = timeit.timeit(stmt=code3, globals=globals(), number=1000)
execution_time4 = timeit.timeit(stmt=code4, globals=globals(), number=1000)
print(f"Execution time using sorted(): {execution_time1} seconds")
print(f"Execution time using dict comprehension: {execution_time2} seconds")
print(f"Execution time using dict() constructor: {execution_time3} seconds")
print(f"Execution time using list of tuples: {execution_time4} seconds")

Saída:

Terminal window
Execution time using sorted(): 6.7144575 seconds
Execution time using dict comprehension: 1.6851845 seconds
Execution time using dict() constructor: 3.5126931 seconds
Execution time using list of tuples: 0.6211229 seconds

Neste exemplo, comparamos o tempo de execução de diferentes abordagens para ordenar um dicionário com 10.000 elementos. Os tempos de execução são medidos em segundos.

Comparando o Desempenho da Ordenação

Além de comparar o desempenho das diferentes estruturas de dados, também é útil comparar o desempenho da ordenação em si. Em Python, a complexidade da ordenação é em média O(n log n), onde n é o número de elementos a serem ordenados. No entanto, o desempenho real pode variar dependendo dos detalhes de implementação.

Para comparar o desempenho da ordenação, você pode usar o módulo timeit para medir o tempo de execução da função sorted() em uma lista de valores em diferentes tamanhos.

import timeit
code1 = '''
sorted_list = sorted(my_list)
'''
code2 = '''
sorted_list = sorted(my_list, reverse=True)
'''
execution_time1 = timeit.timeit(stmt=code1, globals=globals(), number=1000)
execution_time2 = timeit.timeit(stmt=code2, globals=globals(), number=1000)
print(f"Execution time of sorting in ascending order: {execution_time1} seconds")
print(f"Execution time of sorting in descending order: {execution_time2} seconds")

Saída:

Terminal window
Execution time of sorting in ascending order: 0.0483394000000004 seconds
Execution time of sorting in descending order: 0.07278 seconds

Neste exemplo, comparamos o tempo de execução da função sorted() ao ordenar uma lista de diferentes tamanhos em ordem crescente e decrescente. Os tempos de execução são medidos em segundos.

Comparando o Desempenho das Consultas

Além do tempo de ordenação, também é útil comparar o desempenho das consultas a valores em diferentes estruturas de dados. Em Python, as consultas em dicionários são executadas em tempo médio O(1), ou seja, a complexidade é constante, independentemente do tamanho do dicionário.

Vamos comparar o tempo de consulta de um valor específico em um dicionário ordenado, um dicionário não ordenado e uma lista de tuplas usando a função timeit:

import timeit
code1 = '''
value = my_dict.get("middle")
'''
code2 = '''
value = sorted_dict.get("middle")
'''
code3 = '''
value = next(t[1] for t in sorted_list if t[0] == "middle")
'''
execution_time1 = timeit.timeit(stmt=code1, globals=globals(), number=1000000)
execution_time2 = timeit.timeit(stmt=code2, globals=globals(), number=1000000)
execution_time3 = timeit.timeit(stmt=code3, globals=globals(), number=1000000)
print(f"Execution time of querying a value in the original dictionary: {execution_time1} seconds")
print(f"Execution time of querying a value in the sorted dictionary: {execution_time2} seconds")
print(f"Execution time of querying a value in the list of tuples: {execution_time3} seconds")

Saída:

Terminal window
Execution time of querying a value in the original dictionary: 0.12884799999999996 seconds
Execution time of querying a value in the sorted dictionary: 0.22534 seconds
Execution time of querying a value in the list of tuples: 0.5860918000000002 seconds

Neste exemplo, comparamos o tempo de consulta de um valor específico em um dicionário original, um dicionário ordenado e uma lista de tuplas. Os tempos de execução são medidos em segundos.

Conclusão

Neste tutorial, você aprendeu como ordenar um dicionário em Python usando a função sorted() e o parâmetro key. Você também viu como obter visualizações de chaves, valores ou ambos de um dicionário e como especificar uma chave de ordenação para classificar o dicionário por valor, chave ou atributo aninhado. Além disso, você aprendeu a reconstruir um dicionário a partir de uma lista ordenada de chaves e destacou considerações estratégicas e de desempenho ao trabalhar com dicionários ordenados.

Ao fazer tudo isso, você usou o módulo timeit para medir o tempo de execução e comparar o desempenho de diferentes abordagens e estruturas de dados.

Lembre-se de considerar se um dicionário ordenado é realmente necessário para a sua tarefa e de comparar o desempenho e as características das diferentes estruturas de dados disponíveis antes de fazer sua escolha.

Espero que este tutorial tenha sido útil para você entender como ordenar dicionários em Python. Agora você poderá aplicar esse conhecimento em seus próprios projetos Python. Happy coding!