Як я можу повторити словник у відсортованому ключовому порядку в Python?


211

Існує функція, яка закінчується наступним, де dє словник:

return d.iteritems()

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

Відповіді:


171

Не перевіряв це дуже широко, але працює в Python 2.5.2.

>>> d = {"x":2, "h":15, "a":2222}
>>> it = iter(sorted(d.iteritems()))
>>> it.next()
('a', 2222)
>>> it.next()
('h', 15)
>>> it.next()
('x', 2)
>>>

Якщо ви звикли робити for key, value in d.iteritems(): ...замість ітераторів, це все одно працюватиме з рішенням, наведеним вище

>>> d = {"x":2, "h":15, "a":2222}
>>> for key, value in sorted(d.iteritems()):
>>>     print(key, value)
('a', 2222)
('h', 15)
('x', 2)
>>>

З Python 3.x використовуйте d.items()замість d.iteritems()повернення ітератора.


29
використовувати .items()замість iteritems(): як сказав @Claudiu, iteritems не працює для Python 3.x, але items()доступний з Python 2.6.
Ремі

40
Це не очевидно. Насправді items()створюється список і тому використовується пам'ять, тоді як iteritems()по суті не використовує пам'ять. Що використовувати в основному, залежить від розміру словника. Крім того, автоматичний інструмент перетворення Python 2 в Python 3 ( 2to3) автоматично піклується про перетворення з iteritems()на items(), тому не потрібно турбуватися з цього приводу.
Ерік О Лебігот

5
@HowerHell використовуйте collections.OrderedDictпотім ви сортуєте один раз і отримуєте елементи в упорядкованому порядку завжди.
Марк Харвістон

9
Але @EOL, навіть якщо iteritems()не використовує пам'ять, все повинно бути втягнуто в пам'ять sorted(), тому немає різниці між використанням items()і iteritems()тут пам'яттю.
Річард

8
@Richard: Хоча це правда, що всі елементи повинні бути витягнуті в пам'ять, вони зберігаються два рази items()(у списку, поверненому items()та в відсортованому списку) і лише один раз з iteritems()(у відсортованому списку).
Ерік О Лебігот

83

Використовуйте sorted()функцію:

return sorted(dict.iteritems())

Якщо ви хочете фактичний ітератор над відсортованими результатами, оскільки sorted()повертає список, використовуйте:

return iter(sorted(dict.iteritems()))

Для мене це не вдається: <type 'exceptions.TypeError'>: iter () повернув не-ітератор типу 'list'
Майк

Це, мабуть, тому, що ви використовуєте "dict" як ім'я змінної. "dict" - це фактично назва типу словників. Просто вживайте тут іншу назву на зразок "мій суд" і вуаля.
utku_karatas

1
Ще не працює. Ви позитивно сортували () повертає інший ітератор на відміну від звичайного списку?
Майк

коли і де відбувається цей виняток? ви можете переглядати список без проблем

1
Домовились, хоп. Я не думаю, що я ніколи не дзвоню .next () безпосередньо, крім випадків, коли пропускає рядки у файлах. Наше рішення iter (сортується (dict.iteritems ())) закінчується створенням копії всього диктату в пам'яті на "сортованому (") етапі, тому первинна користь ітератора здається втраченою :)

39

Клавіші диктанту зберігаються у хешшлеті так, що це їх "природний порядок", тобто psuedo-random. Будь-яке інше замовлення - це концепція споживача дикту.

sorted () завжди повертає список, а не дікт. Якщо ви передасте йому dict.items () (який створює список кортежів), він поверне список кортежів [(k1, v1), (k2, v2), ...], які можна використовувати у циклі певним чином дуже схожий на дикт, але це ніяк не дикт !

foo = {
    'a':    1,
    'b':    2,
    'c':    3,
    }

print foo
>>> {'a': 1, 'c': 3, 'b': 2}

print foo.items()
>>> [('a', 1), ('c', 3), ('b', 2)]

print sorted(foo.items())
>>> [('a', 1), ('b', 2), ('c', 3)]

Далі виглядає, як диктант у циклі, але це не так, це список кортежів, які розпаковуються в k, v:

for k,v in sorted(foo.items()):
    print k, v

Приблизно еквівалентний:

for k in sorted(foo.keys()):
    print k, foo[k]

Гаразд, але я не хочу Dict чи List, я хочу Iterator. Як я примушую це стати Ітератором?
Майк

2
sorted(foo.keys())краще як еквівалент sorted(foo), оскільки словники повертають свої ключі під час повторної повторення (з перевагою, якщо не змушувати створювати foo.keys()проміжний список, можливо - залежно від того, як sorted()реалізовано для ітерабелів).
Ерік О Лебігот

Цікаво, що краще для швидкості та / або пам'яті, k in sorted(foo.keys()):яка тягне ключі або for k,v in sorted(foo.items()):яка повертає копію списку пар словника, я б здогадавсяsorted(foo.keys())
CrandellWS

1
@CrandellWS: Найкращий спосіб відповісти на запитання про час - це за допомогою модуля Python timeit .
Пітер Роуелл

1
@frank - Короткий відповідь: Ні. Dict - це масив, фактичним ключем є хеш значення наданого ключа. Незважаючи на те, що деякі реалізації можуть бути досить передбачувано, і деякі з них можуть навіть зробити цей контракт, я розраховувати на нічого , коли мова йде про хеш - впорядкованості. Дивіться цю публікацію, щоб отримати докладнішу інформацію про поведінку 3.6+. Зокрема, зверніть увагу на першу відповідь.
Пітер Роуелл

31

Відповідь Грега правильна. Зауважте, що в Python 3.0 вам доведеться це зробити

sorted(dict.items())

як iteritemsне буде.


Для мене це не вдається: <type 'exceptions.TypeError'>: iter () повернув неітератор типу 'list'
Майк

3
"Не використовуйте автомобілі, тому що в майбутньому у нас будуть ховерборди"
JJ

7

Тепер ви також можете використовувати OrderedDictв Python 2.7:

>>> from collections import OrderedDict
>>> d = OrderedDict([('first', 1),
...                  ('second', 2),
...                  ('third', 3)])
>>> d.items()
[('first', 1), ('second', 2), ('third', 3)]

Тут ви знайдете нову сторінку для версії 2.7 та API OrрядDict .


Це поверне ключові значення, значення в тому порядку, в який вони вставлені - не в упорядкованому порядку (тобто за алфавітом).
Тоні Суффолк 66

5

Загалом, такий сорт можна сортувати так:

for k in sorted(d):
    print k, d[k]

Для конкретного випадку у запитанні, маючи "крапку заміни" для d.iteritems (), додайте функцію типу:

def sortdict(d, **opts):
    # **opts so any currently supported sorted() options can be passed
    for k in sorted(d, **opts):
        yield k, d[k]

і так закінчується рядок змінюється від

return dict.iteritems()

до

return sortdict(dict)

або

return sortdict(dict, reverse = True)

5
>>> import heapq
>>> d = {"c": 2, "b": 9, "a": 4, "d": 8}
>>> def iter_sorted(d):
        keys = list(d)
        heapq.heapify(keys) # Transforms to heap in O(N) time
        while keys:
            k = heapq.heappop(keys) # takes O(log n) time
            yield (k, d[k])


>>> i = iter_sorted(d)
>>> for x in i:
        print x


('a', 4)
('b', 9)
('c', 2)
('d', 8)

Цей метод все ще має сортування O (N log N), однак після короткого лінійного перетину він видає елементи в упорядкованому порядку, як це йде, що робить його теоретично більш ефективним, коли не завжди потрібен весь список.



3

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

Я поняття не маю, що є більшим контекстом вашого коду, але ви можете спробувати додати ітератор до результату. як це, можливо ?:

return iter(sorted(dict.iteritems()))

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

наприклад: скажіть, що ваш дикт був: {'a':1,'c':3,'b':2} сортування перетворює його у список:

[('a',1),('b',2),('c',3)]

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


2

Якщо припустити, що ви використовуєте CPython 2.x і маєте великий словник моїх вироків, то використання сортованого (мій вирок) буде повільним, оскільки сортування створює відсортований список ключів мого вироку.

У цьому випадку ви можете подивитися на мій пакет sorteddictупорядкованих вироків, який включає реалізацію C у C. Особливо, якщо вам доведеться кілька разів перебирати відсортований список ключів на різних етапах (тобто кількість елементів) життя словників.

http://anthon.home.xs4all.nl/Python/ordereddict/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.