Conjuntos e dicts são otimizados para diferentes casos de uso. O uso principal de um conjunto é o teste rápido de associação, que é independente da ordem. Para ditados, o custo da pesquisa é a operação mais crítica e é mais provável que a chave esteja presente. Com conjuntos, a presença ou ausência de um elemento não é conhecida antecipadamente e, portanto, a implementação do conjunto precisa otimizar para o caso encontrado e o não encontrado. Além disso, algumas otimizações para operações comuns de conjuntos, como união e interseção, dificultam manter a ordem dos conjuntos sem prejudicar o desempenho.
Embora ambas as estruturas de dados sejam baseadas em hash, é um equívoco comum que os conjuntos sejam implementados apenas como ditados com valores nulos. Mesmo antes da implementação compacta do dict no CPython 3.6, as implementações set e dict já diferiam significativamente, com pouca reutilização de código. Por exemplo, os dictos usam sondagem aleatória, mas os conjuntos usam uma combinação de sondagem linear e endereçamento aberto, para melhorar a localidade do cache. O probe linear inicial (padrão 9 etapas no CPython) verificará uma série de pares de chave / hash adjacentes, melhorando o desempenho, reduzindo o custo do manuseio de colisão de hash - o acesso consecutivo à memória é mais barato que os probes dispersos.
Seria possível, em teoria, alterar a implementação do conjunto do CPython para ser semelhante ao ditado compacto, mas na prática existem desvantagens, e os principais desenvolvedores notáveis se opuseram a fazer essa alteração.
Os conjuntos permanecem desordenados. (Por quê? Os padrões de uso são diferentes. Além disso, implementação diferente.)
- Guido van Rossum
Os conjuntos usam um algoritmo diferente que não é tão alterável para reter a ordem de inserção. As operações de conjunto a conjunto perdem sua flexibilidade e otimizações, se necessário. A matemática dos conjuntos é definida em termos de conjuntos não ordenados. Em resumo, a ordenação de conjuntos não está no futuro imediato.
- Raymond Hettinger
Uma discussão detalhada sobre compactar conjuntos para 3.7 e respostas sobre por que foi decidido não pode ser encontrada nas listas de discussão python-dev.
Em resumo, os pontos principais são que os padrões de uso são diferentes (dados de ordem de inserção como ** kwargs são úteis , menos para conjuntos), a economia de espaço para conjuntos de compactação é menos significativa (porque há apenas matriz de hash e chave) densify, ao contrário de chaves, hashes e valores), e a otimização de sondagem linear acima mencionada em conjuntos é incompatível com uma implementação compacta.
Vou reproduzir o post de Raymond abaixo, que aborda os pontos mais importantes.
Em 14 de setembro de 2016, às 15:50, Eric Snow escreveu:
Então, eu vou fazer o mesmo para conjuntos.
A menos que eu tenha entendido errado, Raymond se opôs a fazer uma mudança semelhante para definir.
Está certo. Aqui estão algumas reflexões sobre o assunto antes de as pessoas começarem a correr soltas.
Para o ditado compacto, a economia de espaço foi uma vitória líquida, com o espaço adicional consumido pelos índices e a alocação geral para as matrizes de chave / valor / hash sendo mais do que compensada pela densidade aprimorada das matrizes de chave / valor / hash. No entanto, para os conjuntos, a rede era muito menos favorável, porque ainda precisamos dos índices e da alocação geral, mas só podemos compensar o custo do espaço densificando apenas duas das três matrizes. Em outras palavras, a compactação faz mais sentido quando você desperdiça espaço para chaves, valores e hashes. Se você perder um desses três, ele deixará de ser atraente.
O padrão de uso para conjuntos é diferente dos dict. O primeiro tem mais pesquisas de acerto ou erro. O último tende a ter menos pesquisas de chave ausentes. Além disso, algumas das otimizações para as operações configuradas tornam difícil manter a ordem dos conjuntos sem afetar o desempenho.
Eu segui um caminho alternativo para melhorar o desempenho do conjunto. Em vez de compactar (que não ganhava muito espaço e incorria no custo de uma indireção adicional), adicionei a análise linear para reduzir o custo de colisões e melhorar o desempenho do cache. Essa melhoria é incompatível com a abordagem de compactação que eu advoguei para dicionários.
Por enquanto, o efeito colateral da encomenda nos dicionários não é garantido, portanto é prematuro começar a insistir que os conjuntos também sejam encomendados. Os documentos já apontam para uma receita para a criação de um OrderedSet (
https://code.activestate.com/recipes/576694/ ), mas parece que a aceitação foi quase zero. Além disso, agora que Eric Snow nos deu um rápido OrderedDict, é mais fácil do que nunca criar um OrderedSet a partir de MutableSet e OrderedDict, mas, novamente, eu não observei nenhum interesse real, porque as análises típicas de dados conjunto a conjunto realmente não precisa ou se preocupa com a encomenda. Da mesma forma, o uso principal de testes rápidos de associação é independente de ordem.
Dito isso, acho que há espaço para adicionar implementações de conjuntos alternativos ao PyPI. Em particular, existem alguns casos especiais interessantes para dados solicitáveis, nos quais as operações de configuração podem ser aceleradas através da comparação de intervalos de chaves inteiros (consulte
https://code.activestate.com/recipes/230113-implementation-of- define-usando-ordenado-listas
para um ponto de partida). IIRC, o PyPI já possui código para filtros de flor tipo set e hash de cuco.
Entendo que é empolgante ter um grande bloco de código aceito no núcleo do Python, mas que não deve abrir portas para envolver-se em reescritas mais importantes de outros tipos de dados, a menos que tenhamos certeza de que isso se justifica.
- Raymond Hettinger
No [Python-Dev], o dict do Python 3.6 se torna compacto e obtém uma versão privada; e as palavras-chave serão ordenadas , setembro de 2016.