Сортировка словаря с использованием operator.itemgetter - PullRequest
19 голосов
/ 14 января 2011

Здесь, на SO , несколько минут назад был задан вопрос о сортировке словарных ключей по их значениям.

Я только что прочитал о методе сортировки operator.itemgetter несколько дней назад и решил попробовать это, но, похоже, он не работает.

Не то чтобы у меня возникли проблемы с ответами, представленными на вопросы, я просто хотел попробовать это с operator.itemgetter.

Таким образом, диктат был:

>>> mydict = { 'a1': ['g',6],
           'a2': ['e',2],
           'a3': ['h',3],
           'a4': ['s',2],
           'a5': ['j',9],
           'a6': ['y',7] }

Я пробовал это:

>>> l = sorted(mydict.itervalues(), key=operator.itemgetter(1))
>>> l
[['e', 2], ['s', 2], ['h', 3], ['g', 6], ['y', 7], ['j', 9]]

И это работает так, как я хочу. Однако, поскольку у меня нет полного словаря (mydict.itervalues()), я попробовал это:

>>> complete = sorted(mydict.iteritems(), key=operator.itemgetter(2))

Это не работает (как я и ожидал).

Итак, как мне отсортировать dict, используя operator.itemgetter и вызвать itemgetter для вложенной пары ключ-значение.

Ответы [ 4 ]

30 голосов
/ 14 января 2011
In [6]: sorted(mydict.iteritems(), key=lambda (k,v): operator.itemgetter(1)(v))
Out[6]: 
[('a2', ['e', 2]),
 ('a4', ['s', 2]),
 ('a3', ['h', 3]),
 ('a1', ['g', 6]),
 ('a6', ['y', 7]),
 ('a5', ['j', 9])]

Ключевым параметром всегда является функция, которая подается по одному элементу из повторяемого (mydict.iteritems()) за один раз.В этом случае элемент может быть чем-то вроде

('a2',['e',2])

Поэтому нам нужна функция, которая может принимать ('a2',['e',2]) в качестве ввода и возвращать 2.

lambda (k,v): ... - это анонимная функция, котораяпринимает один аргумент - 2-кортеж - и распаковывает его в k и v.Поэтому, когда к нашему элементу применяется функция lambda, k будет 'a2', а v будет ['e',2].

lambda (k,v): operator.itemgetter(1)(v), примененный к нашему элементу, таким образом, возвращает operator.itemgetter(1)(['e',2]), который "itemgets" второй элемент в ['e',2], который равен 2.

Обратите внимание, что lambda (k,v): operator.itemgetter(1)(v) не является хорошимспособ кодирования в Python.Как указывает Гнибблер, operator.itemgetter(1) пересчитывается для каждого элемента .Это неэффективно.Смысл использования operator.itemgetter(1) заключается в создании функции, которую можно применять много раз.Вы не хотите заново создавать функцию каждый раз.lambda (k,v): v[1] более читабельно и быстрее:

In [15]: %timeit sorted(mydict.iteritems(), key=lambda (k,v): v[1])
100000 loops, best of 3: 7.55 us per loop

In [16]: %timeit sorted(mydict.iteritems(), key=lambda (k,v): operator.itemgetter(1)(v))
100000 loops, best of 3: 11.2 us per loop
5 голосов
/ 14 января 2011

itemgetter не поддерживает вложенность (хотя attrgetter поддерживает)

вам нужно сгладить подобный дикт

sorted(([k]+v for k,v in mydict.iteritems()), key=itemgetter(2))
5 голосов
/ 14 января 2011

Ответ - ты не можешь.operator.itemgetter(i) возвращает вызываемый объект, который возвращает элемент i своего аргумента, то есть

f = operator.itemgetter(i)
f(d) == d[i]

, он никогда не будет возвращать что-то вроде d[i][j].Если вы действительно хотите сделать это в чисто функциональном стиле, вы можете написать свою собственную compose() функцию:

def compose(f, g):
    return lambda *args: f(g(*args))

и использовать

sorted(mydict.iteritems(), key=compose(operator.itemgetter(1),
                                       operator.itemgetter(1)))

Обратите внимание, что я не рекомендовалсделай это:)

0 голосов
/ 06 декабря 2018

Индексация обычно а-ля kv[1][1] быстрее:

>>> from timeit import timeit
>>> setup = 'import operator; g = operator.itemgetter(1); '
>>> setup += 'd = {i: list(range(i+2)) for i in range(100)}'
>>> kwargs = {'setup': setup, 'number': 10000}

>>> timeit('sorted(d.items(), key=lambda kv: kv[1][1])', **kwargs)
0.5251589557155967

>>> timeit('sorted(d.items(), key=lambda kv: g(kv[1]))', **kwargs)
0.7175205536186695

>>> timeit('sorted(d.items(), key=lambda kv: g(kv)[1])', **kwargs)
0.7915238151326776

>>> timeit('sorted(d.items(), key=lambda kv: g(g(kv)))', **kwargs)
0.9781978335231543
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...