在Python 3中按字典序排序深层嵌套的混合数据类型列表

4 投票
3 回答
663 浏览
提问于 2025-04-18 10:15

在Python 3中,list.sort()这个方法会按照字典顺序进行排序。不过在Python 3里,如果你把一个列表和一个float(浮点数)或int(整数)进行比较,就会出现TypeError的错误。这和Python 2不一样,Python 2里是可以这样做的:

>>> [0, 1] < 2
False

那么,有什么好的办法可以让它像Python 2那样工作呢?

我试过创建一个list的子类,但这样做的话,每个嵌套的列表都必须转换成这个子类的类型,这样所有的嵌套比较才能使用我重写的比较方法。有没有什么办法可以做到这一点,而不需要递归地把每个嵌套列表都转换成子类呢?

我希望能够这样比较两个列表:

>>> a = [[[0, 1], [2, 3]], [0, 1]]
>>> b = [[0, 1], [2, 3]]
>>> a < b
False

结果应该是False,因为a[0][0]是一个list(列表),而b[0][0]是一个int(整数),在我的情况下,int应该总是被认为小于list

编辑:

我想实现一个排序函数,它的功能和Python 3内置的list.sort完全一样,唯一的不同是当一个list和一个floatint进行比较时,这个list应该总是被认为更大。

3 个回答

0

这里有一种比较慢的方法。

如果你想在两个不能直接比较的类型 AB 之间建立顺序,可以把它们的实例放在一个元组里:

a = [[[0, 1], [2, 3]], [0, 1]]
b = [[0, 1], [2, 3]]

def deep_annotate(item):
    if isinstance(item, list):
        return (1, [deep_annotate(i) for i in item])
    else:
        return (0, item)

deep_annotate(a) < deep_annotate(b)
#>>> False

deep_annotate(a) > deep_annotate(b)
#>>> True

不过,这种方法很多时候效率不高,可以通过巧妙使用 cmp_to_key 来提高效率。

0

正确的做法不是去继承 list,而是直接使用 排序方法中的 key 参数,来定义一个自定义的关键函数

sorted(l, key=custom_key_function)

custom_key_function(list_element) 应该为每个列表中的元素生成一个标准化的关键值,确保所有的关键值都是同一种类型。

由于我不知道你的列表可能包含什么样的元素,所以不方便进一步讨论具体的实现细节。不过,从你的例子来看,可能需要使用相同的 custom_key_function 来递归地排序子列表。

1

根据Python 2 的文档

大多数内置类型的对象在比较时,如果它们不是同一个对象,通常会被认为是不相等的;至于一个对象被认为比另一个对象小或大,这个判断是随意的,但在程序的一次执行中是保持一致的。

对象的比较只有在两个对象类型相同的时候才有意义。在程序中不应该依赖像 [0, 1] < 2 这样的表达式返回的值,这也是为什么这个行为在 Python 3 中被去掉了。

进一步解释一下,如果你有一个列表 [[[0, 1], [2, 3]], [0, 1]],它有两个元素:
[[0, 1], [2, 3]] 和 [0, 1]。为了让 Python 对它们进行排序,它会比较它们内部的值,按照字典序进行比较,因为第一个是包含 [0, 1] 和 [2, 3] 的列表,第二个是包含 0 和 1 的列表。但是,接下来它需要比较 [0, 1] 和 0,这两个类型不同,因此比较的结果是随意的。

所以,这种排序是有问题的。

说到这里,如果你有一些可以有意义地排序的列表,还有一些不能排序的(因为上面的原因),一个简单的解决办法是捕获可能出现的异常,然后返回 False。

try:
    [0, 1] < 2
except TypeError:
    # return or assign False. True is not actually meaningful.

或者,对于 list.sort() 方法

try:
    x.sort()
except TypeError:
    pass    # Do nothing. Python would produce meaningless results, anyway.

如果你想要进行有意义的排序(如果这确实有意义的话),那么你需要定义一个键函数,正如之前提到的那样。不过,这可能会比较复杂。也许从不同的角度看待你的问题会更好。

撰写回答