Os dicionários são ordenados no Python 3.6 (pelo menos na implementação do CPython), diferente das encarnações anteriores. Parece uma mudança substancial, mas é apenas um pequeno parágrafo na documentação . Ele é descrito como um detalhe de implementação do CPython, e não como um recurso de linguagem, mas também implica que isso pode se tornar padrão no futuro.
Como a implementação do novo dicionário funciona melhor que a anterior, preservando a ordem dos elementos?
Aqui está o texto da documentação:
dict()
agora usa uma representação "compacta" pioneira em PyPy . O uso de memória do novo dict () é entre 20% e 25% menor comparado ao Python 3.5. O PEP 468 (preservando a ordem de ** kwargs em uma função.) É implementado por isso. O aspecto de preservação de pedidos dessa nova implementação é considerado um detalhe da implementação e não deve ser considerado (isso pode mudar no futuro, mas é desejável que essa nova implementação de ditado no idioma seja liberada algumas vezes antes de alterar as especificações do idioma. para exigir a semântica de preservação de pedidos para todas as implementações atuais e futuras do Python, isso também ajuda a preservar a compatibilidade com versões anteriores da linguagem em que a ordem de iteração aleatória ainda está em vigor, por exemplo, Python 3.5). (Contribuição de INADA Naoki emedição 27350 . Idéia originalmente sugerida por Raymond Hettinger .)
Atualização em dezembro de 2017: dict
a retenção de pedidos de inserção é garantida para Python 3.7
fonte
**kwargs
e,**kwargs
portanto , o texto usado é diplomático: em uma função, a assinatura agora é garantida como um mapeamento de preservação de pedidos de inserção . Eles usaram o termo mapeamento para não forçar outras implementações a ordenar o ditado (e usar umOrderedDict
internamente) e como uma maneira de sinalizar que isso não deve depender do fato de quedict
não foi ordenado.Respostas:
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 :
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.
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 adk_entries
matriz (ou seja, valores que indicam a posição da entrada correspondente emdk_entries
). Essa matriz atua como a tabela de hash. Quando uma chave é hash, ela leva a um dos índices armazenadosdk_indices
e a entrada correspondente é buscada pela indexaçãodk_entries
. Como apenas os índices são mantidos, o tipo dessa matriz depende do tamanho geral do dicionário (variando de tipoint8_t
(1
byte) aint32_t
/int64_t
(4
/8
bytes) em compilações32
/64
bit)Na implementação anterior, uma matriz esparsa de tipo
PyDictKeyEntry
e tamanhodk_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 que2/3 * dk_size
cheia por motivos de desempenho . (e o espaço vazio ainda tinhaPyDictKeyEntry
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 tipoPyDictKeyEntry
paraintX_t
.Portanto, obviamente, criar uma matriz esparsa do tipo
PyDictKeyEntry
exige muito mais memória do que uma matriz esparsa para armazenarint
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.
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.
fonte
entries
lista é redimensionada? ou é mantido um espaço em branco? ou é comprimido de tempos em tempos?DKIX_DUMMY
um valor de-2
e a entrada naentry
matriz é substituída porNULL
, quando a inserção é realizada, os novos valores são anexados à matriz de entradas. Ainda não foi possível discernir, mas, com certeza, quando os índices2/3
estiverem acima do limite, o redimensionamento será realizado. Isso pode levar a um encolhimento, em vez de um aumento, seDUMMY
existirem muitas entradas.d = {i:i for i in range(100)}
você e.pop
todos os itens sem inserção, o tamanho não será alterado. Quando você adiciona novamente,d[1] = 1
o tamanho apropriado é calculado e o ditado é redimensionado.dict
ser ordenado',dict
não são ordenados no sentido em queOrderedDict
são. A questão notável é a igualdade.dict
s têm ordem insensível==
,OrderedDict
s têm ordem sensível. Despejar seOrderedDict
mudardicts
para agora ter comparações sensíveis à ordem pode levar a muitas falhas no código antigo. Eu estou supondo que a única coisa que pode mudar sobreOrderedDict
s é sua implementação.Abaixo está respondendo a primeira pergunta original:
Eu acho que essa frase da documentação é suficiente para responder sua pergunta
dict
não é explicitamente destinado a ser uma coleção ordenada; portanto, se você deseja manter a consistência e não confiar no efeito colateral da nova implementação, deve manter-seOrderedDict
.Faça seu código à prova do futuro :)
Há um debate sobre isso aqui .
EDIT: Python 3.7 manterá isso como um recurso, consulte
fonte
Atualização: Guido van Rossum anunciou na lista de discussão que, a partir do Python 3.7
dict
s em todas as implementações do Python, deve preservar a ordem de inserção.fonte
move_to_end
método e sua igualdade é sensível à ordem: docs.python.org/3/library/… . Veja a nota na resposta de Jim Fasarakis Hilliard.Eu queria acrescentar à discussão acima, mas não tenho reputação de comentar.
O Python 3.8 ainda não foi lançado, mas incluirá a
reversed()
função nos dicionários (removendo outra diferença deOrderedDict
.Eu não vejo nenhuma menção ao operador de igualdade ou outros recursos,
OrderedDict
portanto eles ainda não são totalmente iguais.fonte