Як зберегти ключі / значення в тому ж порядку, що і задекларовано?


320

У мене є словник, який я декларував у певному порядку і хочу постійно його зберігати в такому порядку. Ключі / значення насправді не можна зберігати в порядку, виходячи з їх значення, я просто хочу їх у тому порядку, як я його оголосив.

Тож якщо у мене є словник:

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

Це не в такому порядку, якщо я переглядаю його або повторюю через нього, чи є спосіб переконатися, що Python буде зберігати явний порядок, в якому я оголосив ключі / значення?

Відповіді:


229

З Python 3.6 далі стандартний dictтип підтримує порядок вставки за замовчуванням.

Визначення

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

призведе до словника з ключами у порядку, зазначеному у вихідному коді.

Це було досягнуто за допомогою простого масиву з цілими числами для розрідженої хеш-таблиці, де ці цілі числа індексуються в інший масив, який зберігає пари ключ-значення (плюс обчислений хеш). Цей останній масив просто відбувається для зберігання елементів у порядку вставки, і вся комбінація фактично використовує менше пам'яті, ніж реалізація, що використовується в Python 3.5 і раніше. Докладніше дивіться в оригінальній публікації ідеї Реймонда Хеттінгера .

У 3.6 це все ще вважалося детальною частиною реалізації; дивіться Що нового в Python 3.6 в документації :

Аспект збереження порядку в цій новій реалізації вважається детальною інформацією про реалізацію, і на неї не слід покладатися (це може змінитися в майбутньому, але бажано мати нову реалізацію диктату мовою протягом декількох випусків, перш ніж змінити мовну специфікацію призначати збереження порядку в семантиці для всіх поточних та майбутніх реалізацій Python; це також допомагає зберегти зворотну сумісність із старими версіями мови, де досі діє випадковий порядок ітерації, наприклад, Python 3.5).

Python 3.7 підвищує цю деталь реалізації до мовної специфікації , тому тепер обов'язково dictзберігається порядок у всіх реалізаціях Python, сумісних з цією версією чи новішою. Див . Вимову BDFL .

Ви все ще можете використовувати collections.OrderedDict()клас у певних випадках, оскільки він пропонує додаткову функціональність поверх стандартного dictтипу. Такі як реверсивність (це поширюється на об'єкти перегляду ) та підтримка переупорядкування (за допомогою move_to_end()методу ).


На жаль, це не стосується створення словника . Словники, зазначені як у прикладі в коді, не гарантують збереження того ж порядку. Це означає, що вам потрібно створити порожній дикт і вручну вставити кожен елемент, якщо ви хочете контролювати замовлення.
naught101

8
@ Naught101: ні, це дійсно застосовується до створення Dict. У Python 3.7 та новіших версій за допомогою dict-дисплея, як показано в питанні , гарантовано перераховувати ці клавіші в порядку . Пари ключ-значення, як записані, вставляються зліва направо, тому що мовна специфікація гарантує, що : Якщо задана розділена комою пара ключів / дат, вони оцінюються зліва направо для визначення записів словника . dict()Документація включає в себе навіть приклад.
Martijn Pieters

175
from collections import OrderedDict
OrderedDict((word, True) for word in words)

містить

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

Якщо значення True(або будь-який інший незмінний об'єкт), ви також можете використовувати:

OrderedDict.fromkeys(words, True)

2
Варто зауважити, звичайно, що "незмінна" частина - це не жорстке і швидке правило, яке Python буде застосовувати - це "лише" хороша ідея.
lvc

11
майте на увазі, що такі рішення, як: не OrderedDict(FUTURE=[], TODAY=[], PAST=[])буде працювати, коли згадується про підхід: OrderedDict([('FUTURE', []), ('TODAY', []), ('PAST', [])])будуть підтримувати порядок.
andilabs

2
@andi У мене з’явилася ще одна проблема, коли при використанні jsonify OrdersDict здається втраченим, це порядок, коли генеруються дані json. Чи все це вирішити?
tyan

github.com/pallets/flask/isissue/974 це можна використовувати для вирішення проблеми ..
tyan

5
У Python3.7 тепер за замовчуванням встановлено дікт. mail.python.org/pipermail/python-dev/2017-December/151283.html
sertsedat

167

Замість пояснення теоретичної частини я наведу простий приклад.

>>> 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}

16
Чи існує спосіб масового призначення OrdersDict типу типу Dict?
Тянь

2
OrderedDictдійсно вирішує проблему, але ... в цьому конкретному прикладі ви отримуєте точно такий же результат, використовуючи стандартний словник
Tonechas

2
@Tonechas: Я просто спробував приклад зі стандартним словником і отримав {'aol': 1, 'foo': 3}Тому я думаю, що це хороший наочний приклад.
twasbrillig

4
Урок для всіх: було виявлено (я думаю, що випуск 2.4), що передбачуване хешування Python може призвести до вразливості безпеки , тому зараз немає гарантії, що навіть два різних запуски одного і того ж коду дадуть однакове замовлення у стандарті диктант.
holdenweb

1
@tyan можна викликати OrderedDict.update()з ітератора парами , що містять ключ-значення: d1.upate([(key1, val1), (key2, val2)]).
Ruud Althuizen

37

Зауважте, що ця відповідь стосується версій python до python3.7. CPython 3.6 підтримує порядок вставки за більшості обставин як деталь реалізації. Починаючи з Python3.7 і надалі, було заявлено, що реалізації ПОВИННІ підтримувати порядок вставки, щоб відповідати.


словники python не упорядковані. Якщо ви хочете замовити словник, спробуйте колекції.OrderedDict .

Зауважте, що OrdersDict було введено до стандартної бібліотеки в python 2.7. Якщо у вас є старша версія python, ви можете знайти рецепти впорядкованих словників на ActiveState .


див. пост @ martijn вище. Від python 3.6 і далі dict підтримує впорядкування вставки.
tpk

13

Словники використовуватимуть замовлення, яке робить пошук ефективним, і ви не можете змінити це,

Ви можете просто скористатись списком об’єктів (дворядний елемент, у простому випадку або навіть класом), і додати елементи до кінця. Потім ви можете використовувати лінійний пошук, щоб знайти в ньому елементи.

Можна також створити або використовувати іншу структуру даних, створену з метою підтримання порядку.


Словники використовуватимуть замовлення, яке робить пошук ефективним Нарешті, хтось вказав на це.
scharette

7

Я натрапив на цю посаду, намагаючись зрозуміти, як змусити OrdersDict працювати. PyDev для Eclipse взагалі не міг знайти OrdersDict, тому я, нарешті, вирішив зробити кордону ключових значень мого словника так, як я хотів би їх замовити. Коли мені потрібно було вивести свій список, я просто переглянув значення кортежу і підключив ітераційну «клавішу» зі кортежу до словника, щоб отримати мої значення в тому порядку, в якому я їм потрібен.

приклад:

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

Це дуже громіздко, але я натиснув на час, і це вирішення, яке я придумав.

зауважте: підхід до списків, який запропонував хтось інший, насправді не має для мене сенсу, оскільки списки впорядковані та індексовані (а також є різною структурою, ніж словники).


Прекрасне рішення. Я буду використовувати його для запису json у файл, завжди в одному порядку.
Hrvoje T

6

Ви не можете дійсно робити те, що хочете зі словником. Словник у вас уже d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}створений. Я виявив, що не було можливості утримувати порядок, як тільки він уже створений. Що я зробив, то створив файл json замість об'єкта:

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

Я використав:

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

потім використовується:

print json.dumps(r)

перевірити.


1
То чому б не почати зі списку OrdersDict? Файл JSON насправді нічого тут не додає.
Martijn Pieters

Так, список корисніший для наведення порядку, але відповідь стосувалася питання про впорядкування словників. Просто дайте людям знати про обмеження використання словника та надайте їм можливість подолати, якщо їм потрібно чомусь користуватися словником.
nealous3

1
Але ця частина вже охоплена набагато старшими відповідями, починаючи з 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')])

Основна проблема, яка вже не є диктом, це список кортежів
Олег

2

Іншою альтернативою є використання Pandas, dataframeоскільки це гарантує порядок та розташування індексів елементів у структурі, що нагадує диктант.


1

Як правило, ви можете створити клас , який веде себе як словник, головним чином , бути реалізації методів __contains__, __getitem__, __delitem__, __setitem__і деякі інші. Цей клас може мати будь-яку поведінку, яка вам подобається, наприклад, надаючи відсортований ітератор за клавішами ...


1

якщо ви хочете мати словник у визначеному порядку, ви також можете створити список списків, де перший елемент буде ключовим, а другий - значенням і буде виглядати як цей приклад

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

1
2
2
3

7
Це не "словник", тому що ви не можете шукати елементи за їхнім ключем, не шукаючи всю колекцію (займаючи O (n) час).
BHSPitMonkey

1
Так, це не словник, але, залежно від ситуації, він міг би дати вагоме рішення проблеми оригінального плаката.
SunSparc

він не сказав точно, як ми хотіли, просто те, що хочемо їх замовити =), як завжди існує маса способів зробити одне.
пелос

1

У мене була подібна проблема при розробці проекту «Джанго». Я не міг використати OrрадDict, оскільки я запускав стару версію python, тому рішенням було використовувати клас SortedDict Django:

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

наприклад,

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

Примітка. Ця відповідь спочатку з 2011 р. Якщо у вас є доступ до Python версії 2.7 або новішої, то ви повинні мати доступ до теперішнього стандарту collections.OrderedDict, з якого багато прикладів були надані іншими в цій темі.


0

Ви можете зробити те саме, що я зробив для словника.

Створіть список та порожній словник:

dictionary_items = {}
fields = [['Name', 'Himanshu Kanojiya'], ['email id', 'hima@gmail.com']]
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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.