Приложение Python — полный набор настраиваемых решений для сортировки

Python

Эта статья возникла из личного публичного аккаунта:TechFlow, оригинальность это не просто, прошу внимания


В сегодняшней статье рассказывается о сортировке в Python.Как и многие языки высокого уровня, Python инкапсулирует зрелые функции сортировки. Нам нужно только вызвать внутреннюю функцию сортировки, чтобы завершить сортировку. Однако в реальных сценариях применение сортировки часто бывает более сложным. Например, тип объекта имеет несколько полей. Мы хотим сортировать по указанным полям или мы хотим сортировать по нескольким ключевым словам. В настоящее время это не может быть решено. простыми вызовами функций.


лексикографическая сортировка


Сначала рассмотрим наиболее распространенный сценарий сортировки по словарям Предположим, у нас есть массив словарей с несколькими полями в словаре. Мы хотим иметь возможность сортировать по полю в словаре.В качестве примера возьмем фактические данные:

kids = [
    {'name': 'xiaoming', 'score': 99, 'age': 12},
    {'name': 'xiaohong', 'score': 75, 'age': 13},
    {'name': 'xiaowang', 'score': 88, 'age': 15}
]

Дети здесь представляют собой массив типа dict, а dict имеет три поля: имя, оценка и возраст. Предположим, теперь мы хотим иметь возможность сортировать по счету, что нам делать?

Есть много решений этой проблемы.Во-первых, мы можем использовать анонимную функцию, упомянутую в предыдущей статье, чтобы указать порядок. Использование здесь такое же, как использование приоритетной очереди в предыдущей статье.Давайте посмотрим непосредственно на код:

sorted(kids, key=lambda x: x['score'])

В анонимной функции x, который мы получаем, является элементом в kids, который является dict, поэтому, если мы хотим указать нужное нам поле, нам нужно использовать dict для доступа к элементу, то есть использовать квадратные скобки для найти значение соответствующего поля.

Что, если мы хотим отсортировать по нескольким ключевым словам?

Во-первых, давайте введем сортировку по нескольким ключевым словам или используем приведенные выше данные в качестве примера. В приведенном выше примере баллы у каждого ребенка разные, поэтому результат сортировки определен. Но если есть два человека с одинаковым счетом, и я хочу, чтобы младший пришел первым, что мне делать? Когда мы анализируем его, мы можем обнаружить, что он изначально был отсортирован по баллам от меньшего к большему, но могут быть случаи, когда баллы равны. В настоящее время мы хотим иметь возможность сравнивать возраст по одной и той же оценке, то есть мы хотим сортировать по двум ключевым словам, первое ключевое слово — это оценка, а второе ключевое слово — это возраст.

Поскольку Python поддерживает сортировку типов кортежей и списков, что означает, что мы можем напрямую сравнивать соотношение размеров между [1, 3] и [1, 2], Python будет автоматически сравнивать размер элементов в двух массивах одновременно. . Если он равен, он будет автоматически сравниваться в обратном направлении, пока не будет неравенство или конец.

Как только вы это понимаете, это на самом деле довольно просто. Нам просто нужно немного изменить его в анонимной функции, чтобы добавить поле к возвращаемому ею результату.

sorted(kids, key=lambda x: (x['score'], x['age']))

itemgetter


В дополнение к анонимным функциям в Python также есть собственная библиотека для решения этой проблемы. Использование очень близко к анонимным функциям, и его немного проще использовать.

Это функция получения элементов в библиотеке операторов, давайте посмотрим на код напрямую:

from operator import itemgetter

sorted(kids, key=itemgetter('score'))

Если это несколько ключевых слов, вы можете передать несколько ключей:

sorted(kids, key=itemgetter('score', 'age'))

сортировка объектов


Далее давайте посмотрим на пользовательскую сортировку объектов.Сначала мы напишем приведенный выше dict как объект:

class Kid:
    def __init__(self, name, score, age):
        self.name = name
        self.score = score
        self.age = age

    def __repr__(self):
        return 'Kid, name: {}, score: {}, age:{}'.format(self.name, self.score, self.age)

Чтобы было удобно наблюдать за результатом печати, мы перегрузили метод __repr__, который можно просто рассматривать как метод toString в Java, чтобы мы могли указать результат вывода при его печати.

Точно так же оператор также предоставляет функцию фактора сортировки объекта.Использование такое же, как и у получателя элементов, но имя другое.

from operator import attrgetter

kids = [Kid('xiaoming', 99, 12), Kid('xiaohong', 75, 13), Kid('xiaowang', 88, 15)]

sorted(kids, key=attrgetter('score'))

Мы также можем использовать анонимную функцию lambda для достижения этой цели:

sorted(kids, key=lambda x: x.score)

пользовательская сортировка


Это еще не конец, потому что есть еще некоторые проблемы, которые не могут быть решены. Хоть мы и внедрили сортировку по нескольким ключевым словам, остается нерешаемая проблема, а именно порядок сортировки.

Мы можем передать reverse=True в параметрах отсортированной функции, чтобы контролировать, находится ли она в положительном или обратном порядке, но если я использую несколько ключевых слов, что, если я хочу следовать определенному ключевому слову в порядке возрастания и определенному ключевому слову в порядке убывания? приказ? Например, если мы хотим отсортировать по баллам в порядке убывания, а по возрасту — в порядке возрастания, мы не можем решить это в обратном порядке — это проблема, которая не может быть решена в настоящее время.

Что делать тогда?

В настоящее время необходим окончательный убийца сортировки, а именно пользовательская сортировка, упомянутая в заголовке. То есть мы реализуем функцию, которая определяет размер элемента самостоятельно, а затем позволяем sorted вызывать нашу функцию для завершения сортировки. Это также используется в таких языках, как C++ и Java.

Пользовательские функции написать не сложно, мы можем сделать это по желанию:

def cmp(kid1, kid2):
    return kid1.age < kid2.age if kid1.score == kid2.score else kid1.score > kid2.score

Если вы этого не понимаете, не беда, я написал полную версию:

def cmp(kid1, kid2):
    if kid1.score == kid2.score:
        return kid1.age < kid2.age
    else:
        return kid1.score > kid2.score

После написания это еще не конец, эта функция не может быть использована напрямую, она отличается от анонимной лямбда-функции, о которой мы упоминали ранее. Предыдущая анонимная функция используется только для указания полей, поэтому мы не можем напрямую передать эту функцию ключу, и нам нужно обернуть ее в слой обработки. Однако у этого слоя обработки функции Python уже есть готовые инструменты, мы можем вызвать его напрямую, он есть в functools, давайте посмотрим на код:

from functools import cmp_to_key

sorted(kids, key=cmp_to_key(cmp))

Давайте посмотрим на исходный код функции cmp_to_key:

def cmp_to_key(mycmp):
    """Convert a cmp= function into a key= function"""
    class K(object):
        __slots__ = ['obj']
        def __init__(self, obj):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) < 0
        def __gt__(self, other):
            return mycmp(self.obj, other.obj) > 0
        def __eq__(self, other):
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other):
            return mycmp(self.obj, other.obj) <= 0
        def __ge__(self, other):
            return mycmp(self.obj, other.obj) >= 0
        __hash__ = None
    return K

Мы видим, что внутри функции она фактически определяет класс, затем перегружает функцию сравнения в классе и, наконец, возвращает новый объект, который перегружает функцию сравнения. Эти функции __lt__, __gt__ являются перегруженными функциями сравнения в классе. Например, __lt__ — это функция оценки меньшего, а __eq__ — функция равенства. Итак, вопрос в том, можем ли мы напрямую перегрузить функцию сравнения в классе Kid, чтобы мы могли напрямую сортировать.

Ответ — да, конечно, мы можем это сделать, и на самом деле это очень распространенная практика в объектно-ориентированном программировании. По сравнению с пользовательскими функциями сравнения мы часто предпочитаем определять приоритеты в классах. Метод, реализованный в Python, также очень прост, то есть мы вручную реализуем функцию __lt__, sorted по умолчанию сначала сортирует мелкие элементы, поэтому нам нужно реализовать только функцию __lt__. Параметр, переданный в эту функцию, является другим объектом, мы можем написать логику сравнения прямо в функции. Возвращение True указывает, что текущий объект меньше другого, в противном случае он больше другого.

Прикрепляем полный код:

class Kid:
    def __init__(self, name, score, age):
        self.name = name
        self.score = score
        self.age = age

    def __repr__(self):
        return 'Kid, name: {}, score: {}, age:{}'.format(self.name, self.score, self.age)

    def __lt__(self, other):
        return self.score > other.score or (self.score == other.score and self.age < other.age)

После реализации функции сравнения мы вызываем sorted напрямую, и ее можно отсортировать без каких-либо других аргументов.

Хотя сегодняшний контент не сложный, он очень часто используется в нашем повседневном программировании, и часто бывают ситуации, когда сложные объекты и контент нужно сортировать, поэтому я надеюсь, что каждый сможет его освоить, потому что он обязательно пригодится.

На сегодняшней статье все, если вы чувствуете, что что-то приобрели, пожалуйста, следуйте ей.Отсканируйте код и обратите вниманиеЧто ж, твое маленькое усилие много значит для меня.