【问题标题】:Sorting list by an attribute that can be None按可以为 None 的属性对列表进行排序
【发布时间】:2012-10-19 09:48:31
【问题描述】:

我正在尝试使用

对对象列表进行排序

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

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

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

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

【问题讨论】:

标签: python python-3.x python-2to3


【解决方案1】:

排序比较运算符对 Python 3 中的类型更严格,如 here 所述:

排序比较运算符(=、>)引发 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)

【讨论】:

  • 是的,我已经注意到了。我要的是一个解决方案。如果attr 是一些custom_type,我应该覆盖custom_type.__lt__(),但None 是内置的。
  • 不是我 - 但一项改进是使用 "" 而不是 0 作为 None 替代品,因为 OP 似乎正在比较字符串。
  • 但这会改变被排序对象内部的实际数据,不是吗?
  • @AlexVhr:再次检查编辑以获取不会更改您的列表的解决方案。
  • 它可以工作,但需要对象具有比较方法,例如__lt__() 否则它会导致TypeError: unorderable types: MyObj() &lt; MyObj()
【解决方案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)

【讨论】:

  • 不是很笼统:TypeError: unorderable types: type() &lt; tuple() 如果您的列表包含元组。
  • @neo 你在测试中犯了一个错误。显然,使用此类不会防止其他不相关的类型错误。请注意,类型错误中的两个参数都不是 MinType 类型。
  • 比其他解决方案好得多,恕我直言,而且非常优雅。我一个人永远找不到的那种东西。非常感谢!
  • 请注意,如果您有多个这些对象竞争最小,这可能会导致排序不一致。当两个对象都是MinType 的实例时,一个安全的方法是让__le__ 比较id(self)id(other)
  • @ShioT 目的是将Min 对象用作单例。
【解决方案3】:

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

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 对数字列表进行排序,则不起作用,例如 [3,1,None,2]
  • 如果有负数和非负数,则将None任意放在列表中间,例如[3, 1, None, None, -2, 0]
  • 是的。问题是关于消除错误,并且消除了错误。由于它没有具体说明应该如何处理 None,所以我将 Nones 留在那里。
【解决方案4】:

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

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

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

【讨论】:

  • 你如何对可能没有的数字列表进行排序,(参见对 fralau 的评论)
  • 在回答 alancalvitti 2 岁的问题时 - fralau 在“或”比较语句中利用 None 被视为 False,这是一种有效且简单的方法。如果您希望在数字列表上更明确(假设仅值 > 0 ):my_list.sort(key=lambda x: 0 if x is None else x)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-11
  • 1970-01-01
  • 2011-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多