我的同事Axel Hecht 給我展示了一些我所不知道的關于python排序的東西。 在python里你可以對一個元組進行排序。例子是最好的說明:
1
2
3
|
>>> items = [( 1 , 'B' ), ( 1 , 'A' ), ( 2 , 'A' ), ( 0 , 'B' ), ( 0 , 'a' )] >>> sorted (items) [( 0 , 'B' ), ( 0 , 'a' ), ( 1 , 'A' ), ( 1 , 'B' ), ( 2 , 'A' )] |
默認情況下內置的sort和sorted函數接收的參數是元組時,他將會先按元組的第一個元素進行排序再按第二個元素進行排序。 然而,注意到結果中(0, 'B')在(0, 'a')的前面。這是因為大寫字母B的ASCII編碼比a小。然而,假設你想要一些更人性的排序并且不關注大小寫。你或許會這么做:
1
2
3
4
|
>>> sorted (items, key = str .lower) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> TypeError: descriptor 'lower' requires a 'str' object but received a 'tuple' |
我們將會得到一個錯誤,因為他不能正確處理元組的第一部分。(注:原文作者估計想說元組中第一項是數字,不能使用lower這個方法;正確的原因提示的很明顯了,是因為你傳遞的是一個元組,而元組是沒有l(wèi)ower這個方法的)
我們可以試著寫一個lambda函數(eg.sorted(items, key=lambda x: x.lower() if isinstance(x, str) else x)),他將不會工作因為你只處理了元組的一個元素。(注:同上面,作者這么做必然是錯的,思考給這個lambda傳一個元組,返回的是什么?)
言歸正傳,下面就是你應該怎么做的方法。一個lambda,它會返回一個元組:
1
2
|
>>> sorted (items, key = lambda x: (x[ 0 ], x[ 1 ].lower())) [( 0 , 'a' ), ( 0 , 'B' ), ( 1 , 'A' ), ( 1 , 'B' ), ( 2 , 'A' )] |
現在你完成了它!謝謝Axel的分享!
我確信你知道你可以倒序排列,僅僅使用sorted(items, reverse=True, …),但是你怎么根據關鍵字來進行不同的排序?
使用lambda函數返回元組的技巧,下面是一個我們排序一個稍微高級的數據結構:
1
|
>>> peeps = [{ 'name' : 'Bill' , 'salary' : 1000 }, { 'name' : 'Bill' , 'salary' : 500 }, { 'name' : 'Ted' , 'salary' : 500 }] |
現在,使用lambda函數返回一個元組的特性來排序:
1
2
|
>>> sorted (peeps, key = lambda x: (x[ 'name' ], x[ 'salary' ])) [{ 'salary' : 500 , 'name' : 'Bill' }, { 'salary' : 1000 , 'name' : 'Bill' }, { 'salary' : 500 , 'name' : 'Ted' }] |
很有意思,對吧?Bill 在Ted的前面,并且500在1000的前面。但是如何在相同的 name 下,對 salary 反向排序?很簡單,對它取反:
1
2
|
>>> sorted (peeps, key = lambda x: (x[ 'name' ], - x[ 'salary' ])) [{ 'salary' : 1000 , 'name' : 'Bill' }, { 'salary' : 500 , 'name' : 'Bill' }, { 'salary' : 500 , 'name' : 'Ted' }] |
問題:將列表[[1, 2, 3], [4, 5, 6], [7, 8, 9]]排序為[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
分析:
1.轉變過程如下:
1 2 3 1 4 7
4 5 6 —> 2 5 8
7 8 9 3 6 9
可以將變換過程看成是原二維數組行(row)變成新數組的列(column),即抽出原數組第一行(row)作為第一列(column),第二行(row)作為第二列(column)…當然也可以將變換過程看成是原數組的列變?yōu)樾聰到M的行,限于時間,就暫不考慮這種實現方式。
2.最原始的做法,寫兩個for循環(huán),外層循環(huán)依次迭代數組的行(row),內層循環(huán)迭代數組的列(column),來實現這個反轉過程,將原數組第一行(row)作為第一列(column),第二行(row)作為第二列(column),過程如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
In [ 7 ]: l = [[ 1 , 2 , 3 ], [ 4 , 5 , 6 ], [ 7 , 8 , 9 ]] In [ 8 ]: len_row = 3 In [ 9 ]: len_col = 3 In [ 10 ]: temp = [[],[],[]] In [ 11 ]: for row in l: ....: for i in range (len_col): ....: temp[i].append(row[i]) ....: print temp ....: [[ 1 ], [ 2 ], [ 3 ]] [[ 1 , 4 ], [ 2 , 5 ], [ 3 , 6 ]] [[ 1 , 4 , 7 ], [ 2 , 5 , 8 ], [ 3 , 6 , 9 ]] In [ 12 ]: |
當然,還可以使用列表推導來做,原理和上面一樣,外層迭代row,內層迭代col,生成新的列表:
1
2
3
4
5
|
In [ 100 ]: l Out[ 100 ]: [[ 1 , 2 , 3 ], [ 4 , 5 , 6 ], [ 7 , 8 , 9 ]] In [ 101 ]: [[row[col] for row in l] for col in range ( len (l[ 0 ])) ] Out[ 101 ]: [[ 1 , 4 , 7 ], [ 2 , 5 , 8 ], [ 3 , 6 , 9 ]] |
最后,對這個題目,用zip也可以達到同樣的目的:
1
2
3
4
5
6
7
8
|
In [ 104 ]: l Out[ 104 ]: [[ 1 , 2 , 3 ], [ 4 , 5 , 6 ], [ 7 , 8 , 9 ]] In [ 105 ]: zip ( * l) Out[ 105 ]: [( 1 , 4 , 7 ), ( 2 , 5 , 8 ), ( 3 , 6 , 9 )] In [ 106 ]: map ( list , zip ( * l)) Out[ 106 ]: [[ 1 , 4 , 7 ], [ 2 , 5 , 8 ], [ 3 , 6 , 9 ]] |
*這個符號和列表配合有解壓的意思,如l=[[1, 2, 3], [4, 5, 6], [7, 8, 9]],則我理解*l就變成了[1, 2, 3], [4, 5, 6], [7, 8, 9]這樣三個值,所以zip(*l)和zip([1, 2, 3], [4, 5, 6], [7, 8, 9])的結果才會是一樣的,如下:
1
2
3
4
5
6
7
8
9
|
In [ 17 ]: l = [[ 1 , 2 , 3 ], [ 4 , 5 , 6 ], [ 7 , 8 , 9 ]] In [ 18 ]: zip ([ 1 , 2 , 3 ], [ 4 , 5 , 6 ], [ 7 , 8 , 9 ]) Out[ 18 ]: [( 1 , 4 , 7 ), ( 2 , 5 , 8 ), ( 3 , 6 , 9 )] In [ 19 ]: zip ( * l) Out[ 19 ]: [( 1 , 4 , 7 ), ( 2 , 5 , 8 ), ( 3 , 6 , 9 )] In [ 20 ]: |