我正在尝试使用对对象列表进行排序
my_list.sort(key=operator.attrgetter(attr_name))
但如果任何列表项有attr = None
而不是attr = 'whatever'
,
然后我得到一个TypeError: unorderable types: NoneType() < str()
在 Py2 中这不是问题。我如何在 Py3 中处理这个问题?
我正在尝试使用对对象列表进行排序
my_list.sort(key=operator.attrgetter(attr_name))
但如果任何列表项有attr = None
而不是attr = 'whatever'
,
然后我得到一个TypeError: unorderable types: NoneType() < str()
在 Py2 中这不是问题。我如何在 Py3 中处理这个问题?
排序比较运算符对 Python 3 中的类型更严格,如下所述:
当操作数没有有意义的自然排序时,排序比较运算符(<、<=、>=、>)会引发 TypeError 异常。
Python 2None
在任何字符串(甚至是空字符串)之前排序:
>>> 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)
对于一般解决方案,您可以定义一个比较少于任何其他对象的对象:
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)
此处提出的解决方案有效,但可以进一步缩短:
mylist.sort(key=lambda x: x or 0)
本质上,我们可以将 None 视为其值为 0。
例如:
>>> mylist = [3, 1, None, None, 2, 0]
>>> mylist.sort(key=lambda x: x or 0)
>>> mylist
[None, None, 0, 1, 2, 3]
由于除此之外还有其他东西None
无法与字符串(对于初学者来说是整数和列表)相提并论,因此这里有一个针对一般问题的更强大的解决方案:
my_list.sort(key=lambda x: x if isinstance(x, str) else "")
这将让字符串和任何派生的类型str
作为它们自己进行比较,并将其他所有内容与空字符串进行分类。或者,如果您愿意,也可以替换一个不同的默认默认键,例如,"ZZZZ"
或者chr(sys.maxunicode)
让这些元素在最后进行排序。