Fiquei surpreso ao descobrir recentemente que, embora os dict tenham a garantia de preservar a ordem de inserção no Python 3.7+, os conjuntos não são:
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d
{'a': 1, 'b': 2, 'c': 3}
>>> d['d'] = 4
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> s = {'a', 'b', 'c'}
>>> s
{'b', 'a', 'c'}
>>> s.add('d')
>>> s
{'d', 'b', 'a', 'c'}
Qual é a justificativa para essa diferença? As mesmas melhorias de eficiência que levaram a equipe do Python a alterar a implementação do dict também não se aplicam aos conjuntos?
Não estou procurando ponteiros para implementações de conjuntos ordenados ou maneiras de usar dicts como substitutos de conjuntos. Só estou me perguntando por que a equipe do Python não fez os conjuntos internos preservarem a ordem ao mesmo tempo em que os ditavam.
dict
eset
desde o 2.7.Respostas:
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.
dictobject.c
- mestre , v3.5.9setobject.c
- mestre , v3.5.9Seria 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.
- Guido van Rossum
- 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.
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.
fonte
Discussões
Sua pergunta é pertinente e já foi amplamente discutida em python-devs há pouco tempo. R. Hettinger compartilhou uma lista de razões nesse segmento . O estado do problema parece aberto agora, logo após esta resposta detalhada de T. Peters.
Em suma, a implementação de ditados modernos que preserva a ordem de inserção é única e não é considerada adequada para os conjuntos. Em particular, dicts são usados em todos os lugares para executar o Python (por exemplo,
__dict__
nos namespaces de objetos). Uma grande motivação por trás do ditado moderno era reduzir o tamanho, tornando o Python mais eficiente em termos de memória. Por outro lado, os conjuntos são menos prevalentes que os ditados no núcleo do Python e, assim, dissuadem essa refatoração. Veja também a palestra de R. Hettinger sobre a implementação de ditados modernos.Perspectivas
A natureza não ordenada dos conjuntos no Python é paralela ao comportamento dos conjuntos matemáticos . O pedido não é garantido.
Se qualquer ordem fosse introduzida em conjuntos no Python, esse comportamento obedeceria a uma estrutura matemática completamente separada, a saber, um conjunto ordenado (ou Oset). Osets desempenham um papel separado na matemática, particularmente na combinatória. Uma aplicação prática de Osets é observada na troca de sinos .
Ter conjuntos não ordenados é consistente com uma estrutura de dados muito genérica e onipresente que revela a maioria das matemáticas modernas, ou seja, a Teoria dos Conjuntos . Eu submeto, é bom ter conjuntos não ordenados em Python.
Veja também postagens relacionadas que se expandem sobre este tópico:
fonte