如何利用sorted函数对列表,元组或者对象进行排序

Python中对一个列表或者元组进行排序是很简单的!既然元组基本上可以认为是一个不可以修改的数组,我们几乎可以将其视为一个列表。

对Python列表排序的最简单的方式

如果你只是想对一个数字列表进行排序,Python内置的函数可以帮你实现。

我们有一个数字列表:

>>> a = [3, 6, 8, 2, 78, 1, 23, 45, 9]
我们想对其进行升序排列。我们可以对列表调用sort函数,进行原地排序,或者是调用内置的sorted函数,不修改原始列表,并返回一个新的排好序的列表。两个函数都有相同的参数,基于目的来说可以认为是相同的,除了我们上面提到的不同。

我们试一下:

>>> sorted(a)
[1, 2, 3, 6, 8, 9, 23, 45, 78]
>>>
>>> a.sort()
>>> a
[1, 2, 3, 6, 8, 9, 23, 45, 78]
如果想要实现降序排序呢,如下所示:

>>> sorted(a, reverse=True)
[78, 45, 23, 9, 8, 6, 3, 2, 1]
>>> a.sort(reverse=True)
>>> a
[78, 45, 23, 9, 8, 6, 3, 2, 1]
在这种场景下Python在背后做了什么呢?它在列表中调用一个mergesort的版本。当比较值时,它调用__cmp__函数在每一个对象上,基于__cmp__的返回值,来决定哪一个应该放在另一个的前面。返回值为0代表两个值相等,1代表大于,-1代表小于。后面我们会用到这些信息来做我们自己的对象的排序。

你可能会说,对元组排序又是什么情况呢?我们这就开始介绍。

对Python元组排序的最简单方式

既然元组是一个你不能改变的数组,所以没有一个原地排序的函数可以直接被调用。它们必须使用sorted函数来返回一个列表。记住,这儿是怎么做的:

>>> tup = (3, 6, 8, 2, 78, 1, 23, 45, 9)
>>> sorted(tup)
[1, 2, 3, 6, 8, 9, 23, 45, 78]
注意sorted返回的是一个数组。

好了,我们开始看看如何对更复杂的进行排序。

对列表的列表或者元组的列表进行排序


这有点复杂,但是仍然很简单,所以不用害怕!sorted函数和sort函数都接受一个关键字参数key。

key的作用是提供了一种方式指定一个函数,该函数返回你想要你的元素怎样排序。这个函数有一个"不可见"的参数,代表了一个列表中的元素,并返回一个你想要的元素的key来排序的值。

让我们用示例说明这个key关键字参数!

所以,取一个新的列表,我们想测试根据每个子列表的第一个元素进行排序:

>>> def getKey(item):
...     return item[0]
>>> l = [[2, 3], [6, 7], [3, 34], [24, 64], [1, 43]]
>>> sorted(l, key=getKey)
[[1, 43], [2, 3], [3, 34], [6, 7], [24, 64]]
这里我们看到列表根据子列表的第一个值进行了升序排序。你也可以使用sort函数,但是我个人认为sorted函数更好,所以在后面的例子中我还会使用它。

发生什么?还记得我之前谈到的"不可见"参数吗?那就是每一次sorted需要一个值,传入getKey函数的值。这是一个Python的小技巧。

如果想要对子列表的第二个值进行排序,很简单,只需要改变getKey函数像下面这样:

def getKey(item):
    return item[1]
好了,一切都很好。那如何对元组的列表进行排序呢?很高兴你问了这个!

实际上和我们上面的例子是完全一样的,但是列表定义如下:

>>> a = [(2, 3), (6, 7), (3, 34), (24, 64), (1, 43)]
>>> sorted(l, key=getKey)
[(1, 43), (2, 3), (3, 34), (6, 7), (24, 64)]
唯一改变的是我们现在得到了一个元组的列表,而不是一个列表的列表。

同样的方法也可以作用在元组的元组上,所以我就不深入介绍它了,因为那就显得多余了。

对自定义Python对象的列表进行排序
这里是我自定义的一个对象:

class Custom(object):
    def __init__(self, name, number):
        self.name = name
        self.number = number
为了排序的目的,我们建立一个它们的列表:

customlist = [
    Custom('object', 99),
    Custom('michael', 1),
    Custom('theodore the great', 59),
    Custom('life', 42)
]
好了,我们得到了一个花哨的自定义对象的列表,并且我们想要对它进行排序。我们应该怎么做呢?

好的,我们可以定义一个函数,就像我们上面做的那样,接受元素并返回一个列表。所以我们这么做。

def getKey(custom):
    return custom.number
有一点不同的是,因为我们的对象不再是一个列表。这允许我们根据自定义对象的数字属性进行排序。

所以如果我们在我们的花里胡哨的自定义对象上应用sorted函数,我们得到这些:

>>> sorted(customlist, key=getKey)
[<__main__.Custom object at 0x7f64660cdfd0>,
<__main__.Custom object at 0x7f64660d5050>,
<__main__.Custom object at 0x7f64660d5090>,
<__main__.Custom object at 0x7f64660d50d0>]
一大堆我们看不明白的东西。很好。但是不用担心,亲爱的读者,有一个简单的方法可以作用到我们的自定义对象上,使它看起来好看一些!

我们重新定义对象类:

class Custom(object):
    def __init__(self, name, number):
         self.name = name
         self.number = number
    
    def __repr__(self):
         return '{}: {} {}'.format(self.__class__.__name__,
                                   self.name,
                                   self.number)
好的,我们对它做了什么呢?首先,__repr__函数告诉Python我们想让对象如何被表达。在更复杂的情况下,当它被打印在屏幕上时,它告诉解释器如何显示对象。
现在我们试着对它再一次排序:

>>> sorted(customlist, key=getKey)
[Custom: michael 1, Custom: life 42,
 Custom: theodore the great 59, Custom: object 99]
这看起来好很多了!我们现在实际已经知道排序是正确的!

但是,仍然有一点小问题。看起来有点吹毛求疵,但是我不想每次我想要调用sorted的时候都输入key关键字

我们再次重新定义我们的对象,如下所示:

class Custom(object):
    def __init__(self, name, number):
        self.name = name
        self.number = number
 
    def __repr__(self):
        return '{}: {} {}'.format(self.__class__.__name__,
                                  self.name,
                                  self.number)
 
    def __cmp__(self, other):
        if hasattr(other, 'number'):
            return self.number.__cmp__(other.number)
看起来很好。它所做的是告诉Python如何去比较当前对象的值与在列表中的另一个对象的值。如我所述,sorted函数将会调用__cmp__函数在对象上,为了决定它应该放在那儿根据与其它对象的关系。

现在我们可以调用sorted而不用担心包括key关键字,如下所示:

>>> sorted(customlist)
[Custom: michael 1, Custom: life 42, Custom: theodore the great 59, Custom: object 99]
它工作的很好。请注意所有上面的也作用在自定义对象组成的元组上。但是,正如你知道的,我喜欢节省我的数字树(digital tree)。

对各种各样的Python自定义对象列表的排序
好吧。既然Python是一门动态语言,它并不是很关心我们扔到列表中的是什么对象。它们可以是相同的类型,或者完全不同。

所以,我们定义另一个不同的对象来使用我们的Custom对象。

class AnotherObject(object):
     def __init__(self, tag, age, rate):
          self.tag = tag
          self.age = age
          self.rate = rate

     def __repr__(self):
         return '{}: {} {} {}'.format(self.__class__.__name__,
                                      self.tag,
                                      self.age,
                                      self.rate)

     def __cmp__(self, other):
         if hasattr(other, 'age'):
             return self.age.__cmp__(other.age)
这是一个相似的对象,但是和我们的Custom对象仍然有些不同。
让我们建立一个这些对象与Custom对象的列表:

customlist = [
    Custom('object', 99),
    Custom('michael', 1),
    Custom('theodore the great', 59),
    Custom('life', 42),
    AnotherObject('bananas', 37, 2.2),
    AnotherObject('pants', 73, 5.6),
    AnotherObject('lemur', 44, 9.2)
]
现在我们试着在列表上运行sorted函数:

>>> sorted(customlist)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: an integer is required
我们得到了一个可爱的错误。为什么?因为Custom没有叫做age的属性,AnotherObject没有叫做number的属性。

我们能做什么呢?Panic!

只是开个玩笑而已。我们知道做什么。让我们再次重新顶一个这些对象。

class Custom(object):
    def __init__(self,name,number):
        self.name = name
        self.number = number
 
    def __repr__(self):
        return '{}: {} {}'.format(self.__class__.__name__,
                                  self.name,
                                  self.number)
 
    def __cmp__(self, other):
        if hasattr(other, 'getKey'):
            return self.getKey().__cmp__(other.getKey())
 
    def getKey(self):
        return self.number
 
 
class AnotherObject(object):
    def __init__(self, tag, age, rate):
        self.tag = tag
        self.age = age
        self.rate = rate
 
    def __repr__(self):
        return '{}: {} {} {}'.format(self.__class__.__name__,
                                     self.tag,
                                     self.age, self.rate)
 
    def __cmp__(self, other):
        if hasattr(other, 'getKey'):
            return self.getKey().__cmp__(other.getKey())
 
    def getKey(self):
        return self.age

多么令人惊叹呀!我们刚才做了些什么?我们定义了一个公共的getKey函数,两个对象都有该公共的函数,所以我们可以轻易地进行比较。

现在我们再运行一次sorted函数,我们得到:

>>> sorted(customlist)
[Custom: michael 1, AnotherObject: bananas 37 2.2,
Custom: life 42, AnotherObject: lemur 44 9.2,
Custom: theodore the great 59, AnotherObject: pants 73 5.6,
Custom: object 99]
很好!现在我们的对象可以比较并排序,根据它们核心的内容。

你说你仍然喜欢使用key关键字?
你也可以这么做。如果你省去每个对象中的__cmp__函数,并且在函数外定义类似于下面的函数:

def getKey(customobj):
    return customobj.getKey()
然后像下面这样调用sorted:
>>> sorted(customlist, key=getKey)
[Custom: michael 1, AnotherObject: bananas 37 2.2,
Custom: life 42, AnotherObject: lemur 44 9.2,
Custom: theodore the great 59, AnotherObject: pants 73 5.6,
Custom: object 99]
这里你学会了它。非常简洁,但是并不像一些人想的那样简洁。Python使用内置的sorted函数使他变得很容易。

更多关于排序的想法,可以去看How to sort Python Dictionaries by Key or Value。你也可以通过Lambda Function Syntax (Inline Functions) in Python得到如何根据lambda函数进行排序。

离成为Python专家又更近了一步。




本文翻译自这篇文章

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值