35

我正在尝试使用对对象列表进行排序

my_list.sort(key=operator.attrgetter(attr_name))

但如果任何列表项有attr = None而不是attr = 'whatever',

然后我得到一个TypeError: unorderable types: NoneType() < str()

在 Py2 中这不是问题。我如何在 Py3 中处理这个问题?

4

4 回答 4

34

排序比较运算符对 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)
于 2012-10-19T09:52:48.990 回答
32

对于一般解决方案,您可以定义一个比较少于任何其他对象的对象:

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)
于 2014-10-13T20:47:43.483 回答
12

此处提出的解决方案有效,但可以进一步缩短:

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]
于 2018-05-20T06:48:59.590 回答
3

由于除此之外还有其他东西None无法与字符串(对于初学者来说是整数和列表)相提并论,因此这里有一个针对一般问题的更强大的解决方案:

my_list.sort(key=lambda x: x if isinstance(x, str) else "")

这将让字符串和任何派生的类型str作为它们自己进行比较,并将其他所有内容与空字符串进行分类。或者,如果您愿意,也可以替换一个不同的默认默认键,例如,"ZZZZ"或者chr(sys.maxunicode)让这些元素在最后进行排序。

于 2017-04-25T10:19:09.090 回答