Параметр метода Python — sort имеет тот же приоритет

#python #operator-keyword

#python #operator-ключевое слово

Вопрос:

В моей программе я хочу упорядочить все контакты по их имени, сначала по фамилии, затем по имени.

У меня есть код, который делает это за меня, но он делает это не совсем так, как я хочу.

Например, если у меня есть список имен, упорядоченных в соответствии с текущим кодом, он будет выглядеть следующим образом:

 Luke
Riyaan
Amanda Benson
 

Как вы можете видеть, код по-прежнему принимает None значение для сортировки, чего я хочу, так это:

 Amanda Benson
Luke
Riyaan
 

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

Вот код, который я сейчас использую для сортировки имен:

 import operator

...

addressBook = AddressBook()
addressBook.contactsList
addressBook.contactsList.sort(key = operator.attrgetter("lastName", "firstName"))
 

Комментарии:

1. Какие у вас контакты? Они тоже экземпляры класса? Реализовали ли вы магические методы сравнения в классе?

2. Это экземпляры класса, хранящиеся в списке. Я понятия не имею, что такое / означают магические методы сравнения, так что, вероятно, нет.

Ответ №1:

Тогда вам понадобится пользовательская функция сортировки, которая возвращает ваше имя в качестве первого значения в кортеже, когда последнее имя отсутствует:

 def name_sort(contact):
    if contact.lastName:
        return contact.lastName, contact.firstName
    return contact.firstName, ''

addressBook.contactsList.sort(key=name_sort)
 

Вы можете использовать условное lambda выражение и, чтобы включить его в sort() вызов:

 addressBook.contactsList.sort(key=lambda c: (c.lastName, c.firstName) if c.lastName else (c.firstName, ''))
 

Я создаю здесь кортеж из 2 значений для обоих случаев, но также должно быть достаточно одноэлементного кортежа для случая без фамилии.

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

Для этого вам не нужно реализовывать все методы расширенного сравнения; все, что вам нужно, это один из них, плюс __eq__ (проверка на равенство) и @functools.total_ordering() декоратор класса:

 from functools import total_ordering

@total_ordering
class Contact(object):
    # ...

    def __eq__(self, other):
        if self.lastName != other.lastName:
            return False
        return self.firstName == other.firstName

    def __lt__(self, other):
        if not self.lastName:
            if not other.lastName:
                return self.firstName < other.firstName
            return self.firstName < other.lastName
        return (self.lastName, self.firstName) < (other.lastName, other.firstName)
 

Ответ №2:

Лучший способ сделать это — реализовать расширенные магические методы сравнения в вашем (я думаю) Contact классе:

 class Contact(object):

    def __init__(self, first_name, last_name=""):
        self.first_name = first_name
        self.last_name = last_name

    def __repr__(self): # to make the demo clearer!
        if not self.last_name:
            return str(self.first_name)
        return "{0.first_name} {0.last_name}".format(self)

    def __eq__(self, other):
        return (self.first_name == other.first_name and 
                self.last_name == other.last_name)

    def __lt__(self, other):
        if self.last_name and other.last_name:
            if self.last_name == other.last_name:
                return self.first_name < other.first_name
            return self.last_name < other.last_name
        else:
            if other.last_name:
                return self.first_name < other.last_name
            return self.first_name < other.first_name
 

Теперь вы можете просто использовать sorted и list.sort без аргументов:

 >>> contacts_list = [Contact("Luke"), Contact("Riyaan"), Contact("Amanda", "Benson")]
>>> sorted(contacts_list)
[Amanda Benson, Luke, Riyaan]