Esta é mais uma resposta ao Python 3.41 Um conjunto antes de ser fechado como duplicado.
Os outros estão certos: não confie no pedido. Nem finja que existe um.
Dito isto, há uma coisa em que você pode confiar:
list(myset) == list(myset)
Ou seja, a ordem é estável .
Entender por que existe uma ordem percebida requer entender algumas coisas:
Que o Python usa conjuntos de hash ,
Como o conjunto de hash do CPython é armazenado na memória e
Como os números são divididos
Do topo:
Um conjunto de hash é um método de armazenamento de dados aleatórios com tempos de pesquisa muito rápidos.
Tem uma matriz de apoio:
# A C array; items may be NULL,
# a pointer to an object, or a
# special dummy object
_ _ 4 _ _ 2 _ _ 6
Ignoraremos o objeto fictício especial, que existe apenas para facilitar a remoção, porque não removeremos esses conjuntos.
Para ter uma pesquisa realmente rápida, você faz alguma mágica para calcular um hash de um objeto. A única regra é que dois objetos iguais tenham o mesmo hash. (Mas se dois objetos tiverem o mesmo hash, poderão ser desiguais.)
Em seguida, você cria o índice assumindo o módulo pelo comprimento da matriz:
hash(4) % len(storage) = index 2
Isso torna muito rápido o acesso a elementos.
Hashes são apenas a maior parte da história, como hash(n) % len(storage)
e hash(m) % len(storage)
pode resultar no mesmo número. Nesse caso, várias estratégias diferentes podem tentar resolver o conflito. O CPython usa a "pesquisa linear" 9 vezes antes de fazer coisas complicadas, portanto, ele procurará à esquerda do slot até 9 lugares antes de procurar em outro lugar.
Os conjuntos de hash do CPython são armazenados assim:
Um conjunto de hash pode ter no máximo 2/3 de sua capacidade . Se houver 20 elementos e a matriz de suporte tiver 30 elementos, o armazenamento de backup será redimensionado para ser maior. Isso ocorre porque as colisões são mais frequentes com pequenas lojas de apoio e as colisões tornam tudo mais lento.
A loja de suporte é redimensionada em potências de 4, começando em 8, exceto em conjuntos grandes (elementos de 50 mil) que são redimensionados em potências de dois: (8, 32, 128, ...).
Portanto, quando você cria uma matriz, o armazenamento de backup tem o comprimento 8. Quando estiver 5 cheio e você adicionar um elemento, ele conterá brevemente 6 elementos. 6 > ²⁄₃·8
portanto, isso gera um redimensionamento e a loja de backup quadruplica para o tamanho 32.
Finalmente, hash(n)
apenas retorna n
para números (exceto o -1
que é especial).
Então, vamos olhar para o primeiro:
v_set = {88,11,1,33,21,3,7,55,37,8}
len(v_set)
é 10, portanto, a loja de suporte é pelo menos 15 (+1) depois que todos os itens foram adicionados . A potência relevante de 2 é 32. Portanto, a loja de suporte é:
__ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
Nós temos
hash(88) % 32 = 24
hash(11) % 32 = 11
hash(1) % 32 = 1
hash(33) % 32 = 1
hash(21) % 32 = 21
hash(3) % 32 = 3
hash(7) % 32 = 7
hash(55) % 32 = 23
hash(37) % 32 = 5
hash(8) % 32 = 8
então eles são inseridos como:
__ 1 __ 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
33 ← Can't also be where 1 is;
either 1 or 33 has to move
Então, esperaríamos um pedido como
{[1 or 33], 3, 37, 7, 8, 11, 21, 55, 88}
com o 1 ou 33 que não está no início em outro lugar. Isso usará análise linear, portanto, teremos:
↓
__ 1 33 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
ou
↓
__ 33 1 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
Você pode esperar que o 33 seja o que foi deslocado porque o 1 já estava lá, mas devido ao redimensionamento que acontece enquanto o conjunto está sendo construído, esse não é realmente o caso. Toda vez que o conjunto é reconstruído, os itens já adicionados são efetivamente reordenados.
Agora você pode ver porque
{7,5,11,1,4,13,55,12,2,3,6,20,9,10}
pode estar em ordem. Como existem 14 elementos, a loja de suporte é pelo menos 21 + 1, o que significa 32:
__ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
1 a 13 hash nos primeiros 13 slots. 20 entra no slot 20.
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ __ __ 20 __ __ __ __ __ __ __ __ __ __ __
55 vai no slot hash(55) % 32
que é 23:
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ __ __ 20 __ __ 55 __ __ __ __ __ __ __ __
Se escolhermos 50, esperaríamos
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ 50 __ 20 __ __ __ __ __ __ __ __ __ __ __
E eis que eis:
{1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 20, 50}
#>>> {1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 50, 20}
pop
é implementado simplesmente pela aparência das coisas: ele percorre a lista e abre a primeira.
Isso é tudo detalhe da implementação.