如何利用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]
如果想要實現降序排序呢,如下所示:
在這種場景下Python在背後做了什麼呢?它在列表中呼叫一個mergesort的版本。當比較值時,它呼叫__cmp__函式在每一個物件上,基於__cmp__的返回值,來決定哪一個應該放在另一個的前面。返回值為0代表兩個值相等,1代表大於,-1代表小於。後面我們會用到這些資訊來做我們自己的物件的排序。>>> 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元組排序的最簡單方式
既然元組是一個你不能改變的陣列,所以沒有一個原地排序的函式可以直接被呼叫。它們必須使用sorted函式來返回一個列表。記住,這兒是怎麼做的:
注意sorted返回的是一個數組。>>> tup = (3, 6, 8, 2, 78, 1, 23, 45, 9) >>> sorted(tup) [1, 2, 3, 6, 8, 9, 23, 45, 78]
好了,我們開始看看如何對更復雜的進行排序。
對列表的列表或者元組的列表進行排序
這有點複雜,但是仍然很簡單,所以不用害怕!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函式使他變得很容易。
離成為Python專家又更近了一步。
本文翻譯自這篇文章。