Пользовательские сравнение Функции для сортировки, как предлагается в существующих ответах, позволяют легко сортировать по возрастанию и убыванию, но они имеют серьезные проблемы с производительностью и были удалены в Python 3. оставляя только предпочтительный подход к настройке - пользовательские функции извлечение ключей ... намного быстрее, хотя и более деликатны для использования в относительно редких случаях смешанных восходящих / нисходящих сортов.
В Python 2.*
, который поддерживает любой вид настройки (не и при одном и том же вызове sort
или sorted
:-), пользовательскую функцию сравнения можно передать как cmp=
именованный аргумент; или пользовательская функция извлечения ключа может быть передана как key=
именованный аргумент. В Python 3.*
доступна только последняя опция.
Определенно стоит разобраться с подходом извлечения ключей, даже если вы думаете, что вместо этого решили проблему с помощью пользовательского сравнения: не только для производительности, но и для будущего (Python 3) и для универсальности ( подход key=
также применяется к min
, max
, itertools.groupby
... гораздо более общим, чем подход cmp=
!).
Извлечение ключа очень просто, когда все подполя ключа должны быть отсортированы одинаково (все по возрастанию или все по убыванию) - вы просто извлекаете их; все еще довольно легко, если подполя, которые идут «в другую сторону», являются числами (вы просто меняете их знак при извлечении); деликатный случай - именно тот, который у вас есть - несколько строковых полей, которые необходимо сравнивать противоположными способами.
Достаточно простой подход к решению вашей проблемы - крошечный класс шимов:
class Reverser(object):
def __init__(self, s): self.s = s
def __lt__(self, other): return other.s < self.s
def __eq__(self, other): return other.s == self.s
Обратите внимание, что вам нужно только указать __lt__
и __eq__
(операторы <
и ==
) - sort
, и друзья синтезируют все другие сравнения, если необходимо, на основе этих двух.
Итак, вооружившись этим небольшим вспомогательным инструментом, мы можем легко продолжить ...:
def getkey(tup):
a, b = tup[0].split('_')
return Reverser(a), b
my_list.sort(key=getkey)
Как вы видите, как только вы "получаете" концепции реверсирования и извлечения ключей, вы практически не платите за использование извлечения ключей вместо пользовательского сравнения: код, который я предлагаю, состоит из 4 операторов для класса реверсора (который вы можете написать один раз) и положите в свой модуль «мешок с подарками» где-то три) для функции извлечения ключа и, конечно, один для вызова sort
или sorted
- всего восемь против 4 + 1 == 5 пользовательских подход сравнения в наиболее компактной форме (то есть тот, который использует либо cmp с изменением знака, либо cmp с замененными аргументами). Три утверждения - не слишком большая цена за преимущества извлечения ключей! -)
Производительность, очевидно, не такая уж большая проблема с таким коротким списком, но с еще более скромным (в 10 раз) списком ...:
# my_list as in the Q, my_cmp as per top A, getkey as here
def bycmp():
return sorted(my_list*10, cmp=my_cmp)
def bykey():
return sorted(my_list*10, key=getkey)
...
$ python -mtimeit -s'import so' 'so.bykey()'
1000 loops, best of 3: 548 usec per loop
$ python -mtimeit -s'import so' 'so.bycmp()'
1000 loops, best of 3: 995 usec per loop
То есть, подход key=
уже показывает прирост производительности почти в два раза (сортировка списка в два раза быстрее) при работе со списком из 50 элементов, что стоит скромную цену - 8 строк, а не 5 ", особенно со всеми другими преимуществами, которые я уже упоминал!