关于python:在排序元组时忽略不可排序的数据类型

Ignore unorderable data type while sorting tuple

我正在从一本书中自学python,我一直在做编程练习。练习的目的是列出对象,然后根据一些对象属性对这些对象进行排序。

我的教科书的作者说,在排序过程中,使用一个键来调用类方法来进行每次比较,可以减慢大型数据集的排序时间,并且在构建列表时,通过对每个对象只调用一次类方法来构建"修饰"列表,可以提高后续排序的效率。编程问题规定,我应该生成一系列元组,而不是键,其中tuple元素0是对象属性的文字值,tuple元素1是对象本身。在我有了装饰列表之后,我可以使用内置的列表排序方法(例如"sorted()"或".sort()"等)将其全部排序。

当两个对象具有相同的排序属性值时,我会得到一个错误。此示例代码将重现错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Shrubber:
    def __init__(self, age):
        self.name = 'Roger'
        self.age = age

    def getAge(self):
        return self.age

def main():
    rogerAges = [30, 21, 21, 25]
    rogers = []
    for rAge in rogerAges:
        newShr = Shrubber(rAge)
        rogers.append((newShr.getAge(), newShr))
    rogers.sort()
    print(rogers)

main()

我希望程序打印如下内容:

1
[(21, <__main__.Shrubber object at XxXEX>), (21, <__main__.Shrubber object at YxYEY>), (25, <__main__.Shrubber object at ZxZEZ>), (30, <__main__.Shrubber object at QxQEQ>)]

…但是,它给了我一个类型错误:

1
TypeError: unorderable types: Shrubber() < Shrubber()

我确信我得到了这个错误,因为在Python遇到两个元组元素0的相同值之后,它查找元素1并找到一个无序的数据类型(对象)。然而,两个灌木不能同龄的限制让我看起来好像错过了什么。

我的问题是:我能按tuple元素0稳定地对tuple列表排序,并忽略不可排序的tuple元素1吗?


如果确保比较永远无法通过灌木对象,则不必实现__lt__函数。一种方法是在该元组中插入另一个整数:

1
2
3
4
5
6
7
8
def main():
    rogerAges = [30, 21, 21, 25]
    rogers = list()
    for i, rAge in enumerate(rogerAges):
        newShr = Shrubber(rAge)
        rogers.append((newShr.getAge(), i, newShr))
    rogers.sort()
    print(rogers)

但是,从所有的角度来看,这都是错误的,您应该使用key=函数。这很难阅读,根据我的测量,它大约慢了四倍。不过,使用__lt__(或不推荐使用的cmp=)更糟,大约慢了两倍。

最后一点要注意的是,以这种方式添加到列表中并不是Python式的方式。您可以这样做:

1
2
3
4
5
def main():
    rogerAges = [30, 21, 21, 25]
    rogers = [(rAge, i, Shrubber(rAge)) for (i, rAge) in enumerate(rogerAges)]
    rogers.sort()
    print(rogers)


在这些情况下,我不知道如何让它忽略第二个元素。另一种方法是向对象的类中添加一个方法,该方法将返回所需的信息。这样做可以避免需要passsort()一个key=功能。以下是我的一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Shrubber:
    def __init__(self, age):
        self.name = 'Roger'
        self.age = age

    def getAge(self):
        return self.age

    def __lt__(self, other):  # added comparison method
        return self.age < other.age

def main():
    rogerAges = [30, 21, 21, 25]
    rogers = []
    for rAge in rogerAges:
        newShr = Shrubber(rAge)
        rogers.append((newShr.getAge(), newShr))
    rogers.sort()
    print(rogers)

if __name__ == '__main__':
    main()

输出(为可读性而包装):

1
2
3
4
[(21, <__main__.Shrubber object at 0x00C1D830>),
 (21, <__main__.Shrubber object at 0x00C1D9D0>),
 (25, <__main__.Shrubber object at 0x00C1DA30>),
 (30, <__main__.Shrubber object at 0x00C1D9F0>)]