#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]