Os dicionários são ordenados no Python 3.6+?
Eles são ordenados por inserção [1] . A partir do Python 3.6, para a implementação do Python no CPython, os dicionários lembram a ordem dos itens inseridos . Isso é considerado um detalhe de implementação no Python 3.6 ; você precisa usá- OrderedDict
lo se quiser ordenar por inserção garantida em outras implementações do Python (e outro comportamento ordenado [1] ).
A partir do Python 3.7 , isso não é mais um detalhe de implementação e, em vez disso, se torna um recurso de linguagem. De uma mensagem python-dev da GvR :
Faça assim. "Dict mantém ordem de inserção" é a decisão. Obrigado!
Isso significa simplesmente que você pode depender disso . Outras implementações do Python também devem oferecer um dicionário ordenado por inserção, se desejarem ser uma implementação em conformidade do Python 3.7.
Como a 3.6
implementação do dicionário Python funciona melhor [2] que a anterior, preservando a ordem dos elementos?
Essencialmente, mantendo duas matrizes .
A primeira matriz,, dk_entries
contém as entradas ( do tipoPyDictKeyEntry
) para o dicionário na ordem em que foram inseridas. A ordem de preservação é alcançada por ser uma matriz apenas de acréscimo em que novos itens são sempre inseridos no final (ordem de inserção).
O segundo, dk_indices
contém os índices para a dk_entries
matriz (ou seja, valores que indicam a posição da entrada correspondente em dk_entries
). Essa matriz atua como a tabela de hash. Quando uma chave é hash, ela leva a um dos índices armazenados dk_indices
e a entrada correspondente é buscada pela indexação dk_entries
. Como apenas os índices são mantidos, o tipo dessa matriz depende do tamanho geral do dicionário (variando de tipo int8_t
( 1
byte) a int32_t
/ int64_t
( 4
/ 8
bytes) em compilações 32
/ 64
bit)
Na implementação anterior, uma matriz esparsa de tipo PyDictKeyEntry
e tamanho dk_size
precisava ser alocada; infelizmente, isso também resultou em muito espaço vazio, uma vez que não foi permitido que essa matriz estivesse mais do que 2/3 * dk_size
cheia por motivos de desempenho . (e o espaço vazio ainda tinha PyDictKeyEntry
tamanho!).
Não é o caso agora, pois apenas as entradas necessárias são armazenadas (aquelas que foram inseridas) e uma matriz esparsa do tipo intX_t
( X
dependendo do tamanho do ditado) 2/3 * dk_size
é mantida cheia. O espaço vazio foi alterado de tipo PyDictKeyEntry
para intX_t
.
Portanto, obviamente, criar uma matriz esparsa do tipo PyDictKeyEntry
exige muito mais memória do que uma matriz esparsa para armazenar int
s.
Você pode ver a conversa completa no Python-Dev sobre esse recurso, se estiver interessado, é uma boa leitura.
Na proposta original feita por Raymond Hettinger , pode-se ver uma visualização das estruturas de dados utilizadas, que captura a essência da ideia.
Por exemplo, o dicionário:
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
está atualmente armazenado como [keyhash, key, value]:
entries = [['--', '--', '--'],
[-8522787127447073495, 'barry', 'green'],
['--', '--', '--'],
['--', '--', '--'],
['--', '--', '--'],
[-9092791511155847987, 'timmy', 'red'],
['--', '--', '--'],
[-6480567542315338377, 'guido', 'blue']]
Em vez disso, os dados devem ser organizados da seguinte maneira:
indices = [None, 1, None, None, None, 0, None, 2]
entries = [[-9092791511155847987, 'timmy', 'red'],
[-8522787127447073495, 'barry', 'green'],
[-6480567542315338377, 'guido', 'blue']]
Como você pode ver visualmente agora, na proposta original, muito espaço está essencialmente vazio para reduzir colisões e tornar as pesquisas mais rápidas. Com a nova abordagem, você reduz a memória necessária movendo a dispersão onde realmente é necessária nos índices.
[1]: Eu digo "inserção ordenada" e não "ordenada", pois, com a existência de OrderedDict, "ordenada" sugere um comportamento adicional que o dict
objeto não fornece . OrderedDicts são reversíveis, fornecem métodos sensíveis à ordem e, principalmente, fornecem testes de igualdade sensíveis à ordem ( ==
, !=
). dict
s atualmente não oferecem nenhum desses comportamentos / métodos.
[2]: As novas implementações de dicionário têm melhor desempenho em termos de memória ao serem projetadas de forma mais compacta; esse é o principal benefício aqui. Em termos de velocidade, a diferença não é tão drástica, há lugares em que o novo ditado pode introduzir pequenas regressões ( pesquisas de teclas, por exemplo ), enquanto em outros (iteração e redimensionamento vêm à mente) um aumento de desempenho deve estar presente.
No geral, o desempenho do dicionário, especialmente em situações da vida real, melhora devido à compacidade introduzida.