На этом шаге мы рассмотрим использование функции operator.itemgetter().
У вас есть список словарей, и вы хотите отсортировать записи согласно одному или более значениям.
Сортировка структур этого типа легко выполняется с помощью функции itemgetter() из модуля operator. Предположим, вы выполнили запрос к таблице базы данных, чтобы получить список зарегистрированных пользователей вашего сайта, и получили в ответ вот такую структуру данных:
>>> rows = [
{'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
{'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
{'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]
Можно достаточно легко вывести эти строки с упорядочением по любому из полей, общих для всех словарей. Например:
>>> from operator import itemgetter >>> rows_by_fname = sorted(rows, key=itemgetter('fname')) >>> rows_by_uid = sorted(rows, key=itemgetter('uid')) >>> print(rows_by_fname) >>> print(rows_by_uid) >>>
Вышеприведенный код выведет следующее:
[{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}] [{'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}]
Функция itemgetter() также может принимать несколько ключей. Пример кода:
>>> rows_by_lfname = sorted(rows, key=itemgetter('lname','fname')) >>> print(rows_by_lfname) [{'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}] >>>
В этом примере строки передаются встроенной функции sorted(), которая принимает именованный аргумент key. Этот аргумент должен быть вызываемым объектом, который принимает один элемент из rows и возвращает значение, которое будет использовано в качестве основы для сортировки. Функция itemgetter() создает такой вызываемый объект.
Вызываемый объект - это объект, который имеет метод __call__().
Функция operator.itemgetter() принимает в качестве аргументов индексы, которые используются для извлечения нужных значений из записей в rows. Это может быть ключ словаря, номер элемента в списке или любое другое значение, которое может быть передано методу getitem(). Если вы передадите несколько индексов функции itemgetter(), вызываемый объект, который она создаст, вернет кортеж со всеми элементами, и функция sorted() упорядочит выводимые элементы в соответствии с отсортированным порядком кортежей. Это может быть полезно, если вы хотите провести сортировку сразу по нескольким полям (в примере это имя и фамилия).
Функциональность itemgetter() иногда может быть заменена lambda-выражением. Например:
rows_by_fname = sorted(rows, key=lambda r: r['fname']) rows_by_lfname = sorted(rows, key=lambda r: (r['lname'], r['fname']))
Это решение в большинстве случаев работает отлично. Однако решение с использованием itemgetter() обычно выполняется быстрее. Так что обратите на него внимание, если производительность в приоритете.
Последнее по порядку, но не по значению: не забудьте, что описанная в этом рецепте техника может быть применена к таким функциям, как min() и max(). Например:
>>> min(rows, key=itemgetter('uid')) {'fname': 'John', 'lname': 'Cleese', 'uid': 1001} >>> max(rows, key=itemgetter('uid')) {'fname': 'Big', 'lname': 'Jones', 'uid': 1004} >>>
На следующем шаге мы рассмотрим сортировку объектов, не поддерживающих сравнение.