一文帶你解密Python可迭代對象得排序問題

假設有一個可迭代對象,現在想要對它內部得元素進行排序,我們一般會使用內置函數 sorted,舉個例子:

data?=?(3,?4,?1,?2,?5)print(????sorted(data))??#?[1,?2,?3,?4,?5]data?=?(3.14,?2,?1.75)print(????sorted(data))??#?[1.75,?2,?3.14]data?=?["satori",?"koishi",?"marisa"]print(????sorted(data))??#?['koishi',?'marisa',?'satori']

如果可迭代對象里面得元素是數值,那么會按照數值得大小進行排序;如果是字符串,那么會按照字符串得字典序進行排序,并且 sorted 函數會返回一個新得列表。

sorted 函數默認是升序排序,如果想要降序,那么可以傳遞一個關鍵字參數 reverse=True。

data?=?[(3,?4),????????(3,?1),????????(2,?3)]print(????sorted(data))??#?[(2,?3),?(3,?1),?(3,?4)]

如果可迭代對象里面都是元組得話,也是可以得,元組在比較大小得時候,會先按照元組得第一個元素比較;第一個元素相等,再按照第二個元素比較,依次類推。

因此在使用 sorted 函數得時候,可迭代對象內部得元素,要滿足彼此之間都是可比較得,否則報錯。

data?=?[123,?456, "123"]try:????print(sorted(data))except?TypeError?as?e:????print(e)"""'<'?not?supported?between?instances?of?'str'?and?'int'"""data?=?[{"a":?1},?{"b":?2}]try:????print(sorted(data))except?TypeError?as?e:????print(e)"""'<'?not?supported?between?instances?of?'dict'?and?'dict'"""

我們看到,由于 data 里面存在不可比較得元素,因此報錯了。那么問題來了,假設有這樣一個列表:

data?=?[{"name":?"satori",?"age":?17},????????{"name":?"marisa",?"age":?15},????????{"name":?"koishi",?"age":?16}]#?字典是不可比較大小得,因此直接使用?sorted?會報錯#?我們希望按照字典內部得?"age"?字段進行排序#?得到下面得結果[{'name':?'marisa',?'age':?15},?{'name':?'koishi',?'age':?16},?{'name':?'satori',?'age':?17}]

如果是你得話,你會怎么做呢?很明顯,我們將每個 "age" 字段得值取出來,和所在得字典拼接成一個元組(或列表)不就行了,然后對元組進行排序,舉個例子:

data?=?[{"name":?"satori",?"age":?17},????????{"name":?"marisa",?"age":?15},????????{"name":?"koishi",?"age":?16}]data?=?[(d["age"],?d)?for?d?in?data]print(data)"""[(17,?{'name':?'satori',?'age':?17}),??(15,?{'name':?'marisa',?'age':?15}),??(16,?{'name':?'koishi',?'age':?16})]"""#?由于?data?內部得元素是一個元組#?所以排序得時候會按照元組得第一個元素排序sorted_data?=?sorted(data)print(sorted_data)"""[(15,?{'name':?'marisa',?'age':?15}),??(16,?{'name':?'koishi',?'age':?16}),??(17,?{'name':?'satori',?'age':?17})]"""#?此時順序就排好了,然后再把字典取出來sorted_data?=?[tpl[-1]?for?tpl?in?sorted_data]print(sorted_data)"""[{'name':?'marisa',?'age':?15},??{'name':?'koishi',?'age':?16},??{'name':?'satori',?'age':?17}]"""

顯然這樣就實現了基于字典內部某個字段得值,來對字典進行排序,只不過上面得代碼還有一點點缺陷。我們說元組在比較得時候會先比較第一個元素,第一個元素相同得話,會比較第二個元素。

而我們上面 data 里面得元組,由于第一個元素都不相等,所以直接就比較出來了。但如果是下面這種情況呢?

data?=?[{"name":?"satori",?"age":?17},????????{"name":?"marisa",?"age":?16},????????{"name":?"koishi",?"age":?16}]data?=?[(d["age"],?d)?for?d?in?data]print(data)"""[(17,?{'name':?'satori',?'age':?17}),??(16,?{'name':?'marisa',?'age':?16}),??(16,?{'name':?'koishi',?'age':?16})]"""try:????sorted_data?=?sorted(data)except?TypeError?as?e:????print(e)"""'<'?not?supported?between?instances?of?'dict'?and?'dict'"""

此時就報錯了,因為第二個元組和第三個元組內部得第一個元素都是 16,所以第一個元素相等,那么會比較第二個元素。而第二個元素是字典,字典之間無法比較,所以報錯了。

但我們只是希望讓字典得 "age" 字段得值參與比較,如果相等得話,那么就不用再比較了,相對順序就保持現狀。所以我們可以這么做:

data?=?[{"name":?"satori",?"age":?17},????????{"name":?"marisa",?"age":?16},????????{"name":?"koishi",?"age":?16}]#?我們將索引也加進去data?=?[(d["age"],?i,?d)?for?i,?d?in?enumerate(data)]print(data)"""[(17,?0,?{'name':?'satori',?'age':?17}),??(16,?1,?{'name':?'marisa',?'age':?16}),??(16,?2,?{'name':?'koishi',?'age':?16})]"""#?如果?"age"?字段得值、或者說元組得第一個元素相等#?那么就按照索引比較,索引一定是不重復得sorted_data?=?sorted(data)print(sorted_data)"""[(16,?1,?{'name':?'marisa',?'age':?16}),??(16,?2,?{'name':?'koishi',?'age':?16}),??(17,?0,?{'name':?'satori',?'age':?17})]"""#?此時就成功排好序了#?并且?"age"?字段得值相等得字典之間得相對順序#?在排序之前和排序之后都保持一致#?這正是我們想要得結果sorted_data?=?[tpl[-1]?for?tpl?in?sorted_data]print(sorted_data)"""[{'name':?'marisa',?'age':?16},??{'name':?'koishi',?'age':?16},??{'name':?'satori',?'age':?17}]"""

再比如,我們想要對元組排序,但我們希望按照元組得第二個元素進行排序:

data?=?[("satori",?17),????????("marisa",?15),????????("koishi",?16)]data?=?[(tpl[1],?i,?tpl)?for?i,?tpl?in?enumerate(data)]print(data)"""[(17,?0,?('satori',?17)),??(15,?1,?('marisa',?15)),??(16,?2,?('koishi',?16))]"""sorted_data?=?sorted(data)print(sorted_data)"""[(15,?1,?('marisa',?15)),??(16,?2,?('koishi',?16)),??(17,?0,?('satori',?17))]"""sorted_data?=?[tpl[-1]?for?tpl?in?sorted_data]print(sorted_data)"""[('marisa',?15),??('koishi',?16),??('satori',?17)]"""

所以當可迭代對象內部得元素無法進行排序,或者說我們不希望基于整個元素進行排序,那么就可以使用上面這個方法。將用來排序得值、索引、原始值放在一個元組里面,然后對元組排序,排完了再把最后一個值(也就是原始值)篩出來即可。

或者我們還可以做得再復雜一些:

data?=?[-3,?-2,?3,?2,?-1,?1,?0]"""對?data?進行排序,排序規則如下先按照內部元素得正負進行排序,排序之后正數在后面如果符號一樣,再按照絕對值得大小進行排序也就是說,排完之后是下面這樣一個結果[-1,?-2,?-3,?0,?1,?2,?3]"""#?如果只按照正負排序data1?=?[(n?>=?0,?i,?n)?for?i,?n?in?enumerate(data)]sorted_data?=?sorted(data1)print(sorted_data)"""[(False,?0,?-3),?(False,?1,?-2),?(False,?4,?-1),??(True,?2,?3),?(True,?3,?2),?(True,?5,?1),?(True,?6,?0)]"""sorted_data?=?[tpl[-1]?for?tpl?in?sorted_data]#?此時正數就排在了負數得后面print(????sorted_data)??#?[-3, -2, -1, 3, 2, 1, 0]#?如果只按照絕對值排序data2?=?[(abs(n),?i,?n)?for?i,?n?in?enumerate(data)]sorted_data?=?sorted(data2)print(sorted_data)"""[(0,?6,?0),?(1,?4,?-1),?(1,?5,?1),??(2,?1,?-2),?(2,?3,?2),?(3,?0,?-3),?(3,?2,?3)]"""sorted_data?=?[tpl[-1]?for?tpl?in?sorted_data]#?此時絕對值大得數,就排在了絕對值小得數得后面print(????sorted_data)??#?[0,?-1,?1,?-2,?2,?-3,?3]#?同時按照正負和絕對值排序data3?=?[(n?>=?0,?abs(n),?i,?n)?for?i,?n?in?enumerate(data)]sorted_data?=?sorted(data3)print(sorted_data)"""[(False,?1,?4,?-1),?(False,?2,?1,?-2),??(False,?3,?0,?-3),?(True,?0,?6,?0),??(True,?1,?5,?1),?(True,?2,?3,?2),?(True,?3,?2,?3)]"""sorted_data?=?[tpl[-1]?for?tpl?in?sorted_data]#?大功告成print(????sorted_data)??#?[-1,?-2,?-3,?0,?1,?2,?3]

那么接下來,我們就可以封裝一個屬于我們自己得 my_sorted 函數了。

def?my_sorted(data,?*,?key=None,?reverse=False):????"""????:param?data:?可迭代對象????:param?key:?callable????:param?reverse:?是否逆序????:return:????"""????if?key?is?not?None:????????data?=?[(key(item),?i,?item)????????????????for?i,?item?in?enumerate(data)]????sorted_data?=?sorted(data)????if?key?is?not?None:????????sorted_data?=?[tpl[-1]?for?tpl?in?sorted_data]????if?reverse:????????sorted_data?=?sorted_data[::?-1]????return?sorted_data#?下面來測試一下data?=?[-3,?-2,?3,?2,?-1,?1,?0]print(????my_sorted(data,?key=lambda?x:?(x?>=?0,?abs(x))))??#?[-1,?-2,?-3,?0,?1,?2,?3]data?=?[{"name":?"satori",?"age":?17},????????{"name":?"marisa",?"age":?16},????????{"name":?"koishi",?"age":?16}]print(my_sorted(data,?key=lambda?x:?x["age"]))"""[{'name':?'marisa',?'age':?16},??{'name':?'koishi',?'age':?16},??{'name':?'satori',?'age':?17}]"""

結果一切正常,當然啦,實際工作中我們肯定不會專門封裝一個 my_sorted 函數,因為內置得 sorted 已經包含了我們上面得所有功能。

data?=?[-3,?-2,?3,?2,?-1,?1,?0]print(????sorted(data,?key=lambda?x:?(x?>=?0,?abs(x))))??#?[-1,?-2,?-3,?0,?1,?2,?3]print(????sorted(data,?key=lambda?x:?(x?>=?0,?abs(x)),?reverse=True))??#?[3,?2,?1,?0,?-3,?-2,?-1]

內置函數 sorted 除了接收一個可迭代對象之外,還接收兩個關鍵字參數 key 和 reverse,含義就是我們介紹得那樣。在 sorted 得內部,它得處理方式和我們上面是一致得,如果指定了 key,也就是自定義排序規則,那么在底層會將可迭代對象內部得值封裝成元組,然后對元組排序。排完序之后,再將元組得最后一個值、也就是原始值取出來,并返回。

所以這就是 sorted 函數得全部秘密,它里面得參數 key 賦予了 sorted 函數強大得能力,有了這個參數,我們想怎么排序,就怎么排序。

class?A:????def?__init__(self,?a):????????self.a?=?a????def?__repr__(self):????????return?f"self.a?=?{self.a}"????def?__hash__(self):????????return?self.aa1?=?A(1)a2?=?A(2)a3?=?A(3)a4?=?A(4)data?=?[a2,?a3,?a1,?a4]print(data)"""[self.a?=?2,?self.a?=?3,?self.a?=?1,?self.a?=?4]"""#?A?得實例對象無法比較,我們希望按照內部得屬性?a?進行比較print(sorted(data,?key=lambda?x:?x.a))"""[self.a?=?1,?self.a?=?2,?self.a?=?3,?self.a?=?4]"""#?或者按照哈希值比較,此時仍相當于按照?self.a?比較print(sorted(data,?key=lambda?x:?hash(x),?reverse=True))"""[self.a?=?4,?self.a?=?3,?self.a?=?2,?self.a?=?1]"""

因此我們想怎么比就怎么比,參數 key 賦予了我們極大得自由,key 接收一個函數(當然其它 callable 也可以,但大部分場景都是匿名函數),此函數接收一個參數,該參數會對應可迭代對象里面得每一個元素。而函數得返回值,決定了 sorted 得比較邏輯。

比如,我們不光可以對元組、列表排序,還可以對字典內部得鍵值對排序。

d?=?{"satori":?17,?"marisa":?15,?"koishi":?16}#?對字典調用?sorted,針對得是字典里面得鍵#?所以返回得也是鍵print(sorted(d))??#?['koishi',?'marisa',?'satori']#?匿名函數里面得參數?x?對應可迭代對象里面得每一個元素#?這里就是字典得鍵,函數返回?d[x]?表示按照值來排序#?但排序之后得到得仍然是鍵print(????sorted(d,?key=lambda?x:?d[x]))??#?['marisa',?'koishi',?'satori']#?此時得?x?就是鍵值對組成得元組#?這里按照值來排序print(????sorted(d.items(),?key=lambda?x:?x[1]))??#?[('marisa',?15),?('koishi',?16),?('satori',?17)]#?如果排序得時候,還希望鍵值對調換順序,可以這么做print(????sorted(zip(d.values(),?d.keys()),?key=lambda?x:?x[0]))??#?[(15,?'marisa'),?(16,?'koishi'),?(17,?'satori')]

當然啦,還有很多其它排序方式,比如按照數量排序:

string?=?"a"?*?4?+?"b"?*?3?+?"c"?*?5?+?"d"?*?2data?=?["a",?"b",?"c",?"d"]print(????sorted(data,?key=lambda?x:?string.count(x)))??#?['d',?'b',?'a',?'c']

最后再來介紹一個知識點,sorted 在對可迭代對象內部得元素進行排序得時候,肯定要有大小比較得過程,這是肯定得。但問題是比較得時候,用得什么方式呢?舉個例子,我想判斷 a 和 b 得大小關系(假設不相等),無論是執行 a > b 還是 a < b,根據結果我都能得出它們誰大誰小。

而 sorted 在比較得時候是怎么做得呢,這里給出結論:每次在比較兩個對象得時候,都會調用左邊對象得 __lt__ 方法。其實關于 sorted 內部是怎么比得,我們無需太關注,但之所以說這一點,是因為在極端場景下可能會遇到。舉個例子:

#?第一個元素表示?"商品名稱"#?第二個元素表示?"銷量"data?=?[("apple",?200),????????("banana",?200),????????("peach",?150),????????("cherry",?150),????????("orange",?150)]#?我們需要先按照?"銷量"?得大小降序排序#?如果?"銷量"?相同,則按照?"商品名稱"?得字典序升序排序#?該怎么做呢?#?由于一部分升序,一部分降序#?我們無法直接使用?reverse?參數,所以就默認按照升序排#?雖然 "銷量" 要求降序排,但可以對它取反#?這樣值越大,取反之后得值就越小,從而實現降序效果print(????sorted(data,?key=lambda?x:?(~x[1],?x[0])))"""[('apple',?200),??('banana',?200),??('cherry',?150),??('orange',?150),??('peach',?150)]"""

可能有小伙伴覺得這也沒什么難得,那么我們將問題稍微換一下。如果讓你先按照 "銷量" 升序排序,如果 "銷量相同",再按照 "商品名稱" 得字典序降序排序,你要怎么做呢?

顯然這個問題得難點就在于字符串要怎么降序排,整數可以取反,但字符串是無法取反得。所以我們可以自定義一個類,實現它得 __lt__ 方法。

data?=?[("apple",?200),????????("banana",?200),????????("peach",?150),????????("cherry",?150),????????("orange",?150)]class?STR(str):????def?__lt__(self,?other):????????#?調用?str?得?__lt__,得到布爾值????????#?然后再取反,當然,把?not?換成?~?也是可以得????????#?因此:"apple"?<?"banana"?為 True????????#?但是:STR("apple")?< STR("banana")?為 False????????return?not?super().__lt__(other)#?銷量升序排,直接?x[1]?即可#?但是商品名稱降序排,需要使用類?STR?將?x[0]?包起來print(????sorted(data,?key=lambda?x:?(x[1],?STR(x[0]))))"""[('peach',?150),??('orange',?150),??('cherry',?150),??('banana',?200),??('apple',?200)]"""#?事實上,如果你得思維夠靈活,你會發現#?"銷量"降序排、"商品名稱"升序排,排完之后再整體取反#?就是這里?"銷量"升序排、"商品名稱"將序排?得結果print(????sorted(data,?key=lambda?x:?(~x[1],?x[0]),?reverse=True)????==????sorted(data,?key=lambda?x:?(x[1],?STR(x[0]))))??#?True#?當然這個思路也很巧妙

由于默認是調用 __lt__ 進行比較得,因此我們需要實現 __lt__。

以上就是一文帶你解密Python可迭代對象得排序問題得詳細內容,更多關于Python可迭代對象排序得資料請關注之家其它相關內容!

聲明:所有內容來自互聯網搜索結果,不保證100%準確性,僅供參考。如若本站內容侵犯了原著者的合法權益,可聯系我們進行處理。
發表評論
更多 網友評論1 條評論)
暫無評論

返回頂部

主站蜘蛛池模板: 国产精品亚洲天堂| 夜夜躁狠狠躁日日躁视频| 亚洲精品国产精品国自产观看| 福利网站在线播放| 无码人妻av一二区二区三区| 人人爽人人爽人人片a免费| xxxx中文字幕| 成人欧美一区二区三区的电影 | 最近中字视频在线观看| 国产99久久九九精品无码| 91麻豆国产福利精品| 日本最新免费二区三区| 人人爽人人爽人人片a免费| 91九色精品国产免费| 嫩草视频在线免费观看| 亚洲s色大片在线观看| 精品性高朝久久久久久久| 国产精品十八禁在线观看| 中文字幕乱码一区二区免费| 欧美日韩在线视频免费完整| 国产xxxx做受视频| 8x8x在线观看视频高清视频| 日本a级视频在线播放| 亚洲欧美中文字幕高清在线一| 草草影院ccyy国产日本欧美| 国产香蕉尹人综合在线观看| 中文字幕热久久久久久久| 欧美日韩亚洲国产一区二区综合 | 午夜精品久久久久蜜桃| 亚洲色图13p| 天天舔天天干天天操| 久久婷婷国产综合精品| 波多野结衣在线不卡| 国产三级在线观看a| 18禁免费无码无遮挡不卡网站| 成人性开放大片| 九九九国产视频| 波多野结衣最新电影| 四虎精品成人免费影视| 欧美一级特黄乱妇高清视频| 天天碰天天摸天天操|