Como manter chaves / valores na mesma ordem que declarada?

320

Eu tenho um dicionário que declarei em uma ordem específica e quero mantê-lo nessa ordem o tempo todo. As chaves / valores não podem realmente ser mantidos em ordem com base em seu valor, eu só quero na ordem em que o declarei.

Então, se eu tenho o dicionário:

d = {'ac': 33, 'gw': 20, 'ap': 102, 'za': 321, 'bs': 10}

Não está nessa ordem, se eu o visualizar ou iterar, existe alguma maneira de garantir que o Python mantenha a ordem explícita em que declarei as chaves / valores?

roflwaffle
fonte

Respostas:

229

A partir do Python 3.6 em diante, o dicttipo padrão mantém a ordem de inserção por padrão.

Definindo

d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}

resultará em um dicionário com as chaves na ordem listada no código-fonte.

Isso foi alcançado usando uma matriz simples com números inteiros para a tabela de hash esparsa, onde esses números inteiros indexam em outra matriz que armazena os pares de valores-chave (mais o hash calculado). Esse último array apenas armazena os itens em ordem de inserção e toda a combinação realmente usa menos memória do que a implementação usada no Python 3.5 e anteriores. Veja o post de idéias original de Raymond Hettinger para obter detalhes.

Na versão 3.6, isso ainda era considerado um detalhe de implementação; ver o que há de novo em Python 3.6 documentação :

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).

O Python 3.7 eleva esses detalhes de implementação a uma especificação de linguagem ; portanto, agora é obrigatório dictpreservar a ordem em todas as implementações do Python compatíveis com essa versão ou mais recente. Veja o pronunciamento do BDFL .

Você ainda pode usar a collections.OrderedDict()classe em alguns casos, pois ela oferece algumas funcionalidades adicionais sobre o dicttipo padrão . Como reversível (isso se estende aos objetos da visualização ) e suporte à reordenação (por meio do move_to_end()método ).

Martijn Pieters
fonte
Infelizmente, isso não é relevante para a criação de dicionário . Os dicionários especificados como o exemplo no código não são garantidos para manter a mesma ordem. Isso significa que você precisa criar um ditado vazio e inserir manualmente cada elemento se desejar controlar o pedido.
naught101
8
@ naught101: não, isso se aplica à criação de ditados. No Python 3.7 e superior, é garantido que o uso de uma exibição dict, como mostrado na pergunta, lista essas chaves em ordem . Os pares de valores-chave conforme gravados são inseridos da esquerda para a direita, porque a especificação do idioma garante que : Se uma sequência separada por vírgula de pares de chave / dados é fornecida, eles são avaliados da esquerda para a direita para definir as entradas do dicionário . A dict()documentação ainda inclui um exemplo.
Martijn Pieters
175
from collections import OrderedDict
OrderedDict((word, True) for word in words)

contém

OrderedDict([('He', True), ('will', True), ('be', True), ('the', True), ('winner', True)])

Se os valores forem True(ou qualquer outro objeto imutável), você também poderá usar:

OrderedDict.fromkeys(words, True)
eumiro
fonte
2
Vale ressaltar, é claro, que a parte 'imutável' não é uma regra rígida e rápida que o Python aplicará - é "apenas" uma boa idéia.
LVC
11
esteja ciente de que soluções como: OrderedDict(FUTURE=[], TODAY=[], PAST=[])não funcionam, quando mencionadas: OrderedDict([('FUTURE', []), ('TODAY', []), ('PAST', [])])manterão a ordem.
andilabs
2
@andi Eu tenho outro problema, ao usar o jsonify, o OrderedDict parece perder a ordem ao gerar os dados do json. De qualquer forma, para resolver isso?
tyan 31/03
github.com/pallets/flask/issues/974 isso pode ser usado para resolver o problema ..
tyan
5
O Python3.7 agora tem o comando ordenado por padrão. mail.python.org/pipermail/python-dev/2017-December/151283.html
sertsedat 4/18/18
167

Em vez de explicar a parte teórica, darei um exemplo simples.

>>> from collections import OrderedDict
>>> my_dictionary=OrderedDict()
>>> my_dictionary['foo']=3
>>> my_dictionary['aol']=1
>>> my_dictionary
OrderedDict([('foo', 3), ('aol', 1)])
>>> dict(my_dictionary)
{'foo': 3, 'aol': 1}
Mohit Dabas
fonte
16
Existe uma maneira de atribuir massa OrderedDict como o tipo Dict?
tyan
2
OrderedDictde fato resolve o problema, mas ... neste exemplo em particular que você obtenha exatamente o mesmo resultado usando um dicionário padrão
Tonechas
2
@ Tonechas: Eu apenas tentei o exemplo com um dicionário padrão e consegui. {'aol': 1, 'foo': 3}Então, acho que é um bom exemplo ilustrativo.
Twsbrillig #
4
Há uma lição para todos: foi descoberto (acho que na versão 2.4) que o hash previsível do Python pode dar origem a vulnerabilidades de segurança , então agora não há garantia de que mesmo duas execuções diferentes do mesmo código dê a mesma ordem em um padrão ditado.
Holdenweb 1/09/16
1
@tyan você pode chamar OrderedDict.update()com um iterável contendo pares chave-valor: d1.upate([(key1, val1), (key2, val2)]).
Ruud Althuizen 12/09
37

Observe que esta resposta se aplica às versões python anteriores ao python3.7. O CPython 3.6 mantém a ordem de inserção na maioria das circunstâncias como um detalhe de implementação. A partir do Python3.7 em diante, foi declarado que as implementações DEVEM manter a ordem de inserção para serem compatíveis.


dicionários python não são ordenados. Se você deseja um dicionário ordenado, tente collections.OrderedDict .

Observe que o OrderedDict foi introduzido na biblioteca padrão no python 2.7. Se você possui uma versão mais antiga do python, pode encontrar receitas para dicionários ordenados no ActiveState .

mgilson
fonte
veja a publicação de @ martijn acima. A partir do python 3.6 em diante, o dict suporta pedidos de inserção.
TPK
13

Os dicionários usarão um pedido que tornará a pesquisa eficiente e você não poderá alterá-lo,

Você pode simplesmente usar uma lista de objetos (uma tupla de 2 elementos em um caso simples ou mesmo uma classe) e anexar itens ao final. Você pode usar a pesquisa linear para encontrar itens nela.

Como alternativa, você pode criar ou usar uma estrutura de dados diferente criada com a intenção de manter a ordem.

Fire Lancer
fonte
Os dicionários usarão uma ordem que torne a pesquisa eficiente Finalmente, alguém apontou.
Scharette 27/11/19
7

Me deparei com este post enquanto tentava descobrir como fazer o OrderedDict funcionar. O PyDev para Eclipse não conseguiu encontrar o OrderedDict, então acabei decidindo fazer uma tupla dos valores-chave do meu dicionário, como gostaria que eles fossem ordenados. Quando eu precisava exibir minha lista, apenas iterava os valores da tupla e conectei a 'chave' iterada da tupla no dicionário para recuperar meus valores na ordem em que eu precisava deles.

exemplo:

test_dict = dict( val1 = "hi", val2 = "bye", val3 = "huh?", val4 = "what....")
test_tuple = ( 'val1', 'val2', 'val3', 'val4')
for key in test_tuple: print(test_dict[key])

É um pouco complicado, mas estou com pressa de tempo e é a solução alternativa que inventei.

nota: a lista de listas que alguém sugeriu não faz muito sentido para mim, porque as listas são ordenadas e indexadas (e também são uma estrutura diferente dos dicionários).

smushface
fonte
Ótima solução. Vou usá-lo para escrever json em arquivo, sempre na mesma ordem.
precisa
6

Você não pode realmente fazer o que quiser com um dicionário. Você já tem o dicionário d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}criado. Descobri que não havia como manter a ordem, uma vez que ela já foi criada. O que eu fiz foi criar um arquivo json com o objeto:

{"ac":33,"gw":20,"ap":102,"za":321,"bs":10}

Eu usei:

r = json.load(open('file.json'), object_pairs_hook=OrderedDict)

então usado:

print json.dumps(r)

verificar.

nealous3
fonte
1
Então, por que não começar com um OrderedDict de uma lista? O arquivo JSON realmente não adiciona nada aqui.
Martijn Pieters
Sim, a lista é mais útil para manter a ordem, mas a resposta foi em relação à pergunta sobre pedidos de dicionários. Apenas informe as pessoas sobre as limitações do uso de um dicionário e dê a elas uma possível solução, caso precisem usar um dicionário por algum motivo.
nealous3
1
Mas essa parte já está coberta por respostas muito mais antigas, que datam de 2012.
Martijn Pieters
3
from collections import OrderedDict
list1 = ['k1', 'k2']
list2 = ['v1', 'v2']
new_ordered_dict = OrderedDict(zip(list1, list2))
print new_ordered_dict
# OrderedDict([('k1', 'v1'), ('k2', 'v2')])
user11502624
fonte
problema principal que não é mais um ditado, é uma lista de tuplas
Oleg
2

Outra alternativa é usar o Pandas, dataframepois ele garante a ordem e a localização do índice dos itens em uma estrutura semelhante a um ditado.

Ville
fonte
1

Geralmente, você pode criar uma classe que se comporta como um dicionário, seja principalmente da implementação dos métodos __contains__, __getitem__, __delitem__, __setitem__e alguns mais. Essa classe pode ter qualquer comportamento que você desejar, por exemplo, privando um iterador classificado sobre as chaves ...

Rasmus Kaj
fonte
1

se você deseja ter um dicionário em uma ordem específica, também pode criar uma lista de listas, onde o primeiro item será a chave e o segundo item será o valor e será semelhante a este exemplo

>>> list =[[1,2],[2,3]]
>>> for i in list:
...     print i[0]
...     print i[1]

1
2
2
3
pelos
fonte
7
Esse não é um "dicionário" porque você não pode procurar itens pela chave deles sem pesquisar a coleção inteira (demorando O (n)).
BHSPitMonkey
1
Sim, não é um dicionário, mas, dependendo da situação, poderia fornecer uma solução válida para o problema do pôster original.
SunSparc 25/08/2015
ele não disse exatamente como queríamos, apenas que quer poder solicitá-los =), pois sempre há muitas maneiras de fazer uma coisa.
Pelos
1

Eu tive um problema semelhante ao desenvolver um projeto Django. Não pude usar o OrderedDict, porque estava executando uma versão antiga do python, então a solução foi usar a classe SortedDict do Django:

https://code.djangoproject.com/wiki/SortedDict

por exemplo,

from django.utils.datastructures import SortedDict
d2 = SortedDict()
d2['b'] = 1
d2['a'] = 2
d2['c'] = 3

Nota: Esta resposta é originalmente de 2011. Se você tiver acesso ao Python versão 2.7 ou superior, deverá ter acesso ao padrão agora collections.OrderedDict, dos quais muitos exemplos foram fornecidos por outras pessoas neste segmento.

Evan B.
fonte
0

Você pode fazer a mesma coisa que fiz no dicionário.

Crie uma lista e um dicionário vazio:

dictionary_items = {}
fields = [['Name', 'Himanshu Kanojiya'], ['email id', '[email protected]']]
l = fields[0][0]
m = fields[0][1]
n = fields[1][0]
q = fields[1][1]
dictionary_items[l] = m
dictionary_items[n] = q
print dictionary_items
Himanshu Kanojiya
fonte