Сортировка списка по атрибуту, который может быть None
Я пытаюсь отсортировать список объектов с помощью
my_list.sort(key=operator.attrgetter(attr_name))
но если какой-либо из элементов списка имеет attr = None
вместо attr = 'whatever'
,
то я получаю a TypeError: unorderable types: NoneType() < str()
В Py2 это не проблема. Как мне обрабатывать это в Py3?
Ответы
Ответ 1
Операторы сравнения порядка более строгие относительно типов в Python 3, как описано здесь:
Операторы сравнения порядка (<, < =, > =, > ) создают TypeError исключение, когда операнды не имеют значимого естественного упорядочения.
Python 2 сортирует None
перед любой строкой (даже пустой строкой):
>>> None < None
False
>>> None < "abc"
True
>>> None < ""
True
В Python 3 любые попытки упорядочения экземпляров NoneType
приводят к исключению:
>>> None < "abc"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: NoneType() < str()
Самое быстрое исправление, о котором я могу думать, это явно сопоставить экземпляры None
с чем-то упорядоченным, например ""
:
my_list_sortable = [(x or "") for x in my_list]
Если вы хотите сортировать свои данные, сохраняя их неповрежденными, просто дайте sort
индивидуальный метод key
:
def nonesorter(a):
if not a:
return ""
return a
my_list.sort(key=nonesorter)
Ответ 2
Для общего решения вы можете определить объект, который сравнивается меньше любого другого объекта:
from functools import total_ordering
@total_ordering
class MinType(object):
def __le__(self, other):
return True
def __eq__(self, other):
return (self is other)
Min = MinType()
Затем используйте ключ сортировки, который заменяет Min
для любых значений None
в списке
mylist.sort(key=lambda x: Min if x is None else x)
Ответ 3
Так как есть другие вещи помимо None
, которые не сопоставимы с строкой (ints и lists, for starters), вот более надежное решение общей проблемы:
my_list.sort(key=lambda x: x if isinstance(x, str) else "")
Это приведет к тому, что строки и любой тип, полученные из str
, будут сравниваться как сами по себе, а также все остальное с пустой строкой. Или замените другой стандартный по умолчанию ключ, если хотите, например. "ZZZZ"
или chr(sys.maxunicode)
, чтобы сделать такие элементы сортированными в конце.