2012年5月29日 星期二

[Python]如何在Python排序(Python Sorting)

Python提供兩種內建排序的function分別是sort()和sorted()
這兩個function都可以用來排序一個list
差別在於sorted()會回傳一個排序好新的list
sort()會直接修改原始的list並排序完成
以下是一個範例:
>>> a = [2, 4, 3, 5, 1]
>>> sorted(a)
[1, 2, 3, 4, 5]
>>> a
[2, 4, 3, 5, 1]
>>> a.sort()
>>> print a
[1, 2, 3, 4, 5]
從以上範例可以知道,如果你確定原始的list不需要保留下來的話,可以使用sort()來排序
如果要保留原本的list,就用sorted()來產生一個排序好的新list

這兩個function還有另一個不一樣的地方
sort()只能用在list上排序,而sorted()可用來排序任何的iterable(string, dictionary, tuple...)
下列範例用sorted()來排序一個dictionary
>>> sorted({1: 'D', 3: 'B', 2: 'B', 6: 'E', 5: 'A'})
[1, 2, 3, 5, 6]

sorted()提供了一些參數可以讓我們決定要怎麼排序
當我們把reverse設成True的時候,會把排序的結果由預設的ascending改成descending
strs = ['aa', 'BB', 'zz', 'CC']
print sorted(strs)  # ['BB', 'CC', 'aa', 'zz'] 
print sorted(strs, reverse=True)   # ['zz', 'aa', 'CC', 'BB']
可以設定一個function給key這個參數,在排序之前會自動對每個element call一次key所指定的function
透過這個方法可以做一些比較複雜一點的排序
sorted("aa BB cc ZZ".split())                       #['BB', 'ZZ', 'aa', 'cc']   (case sensitive)
sorted("aa BB cc ZZ".split(), key=str.lower)        #['aa', 'BB', 'cc', 'ZZ']   (case-insensitive)
如果要根據某些欄位來做sort,也可以用key這個參數
要注意的是,key只接受function當作參數。下列範例使用lambda來傳入一個匿名函式
students = [
        ('john', 'A', 15),
        ('jane', 'B', 12),
        ('dave', 'B', 10),
]
sorted(students, key = lambda x : x[2])   # sort by age
#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
為了方便使用者使用,Python提供operator module,不用自己撰寫key的function
以下是使用operator module中的itemgetter()的範例
from operator import itemgetter, attrgetter
sorted(student_tuples, key=itemgetter(2))
#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
如果sort的物件是class object的話,也可以使用operator module中的attrgetter()來做sort
class Student:
        def __init__(self, name, grade, age):
                self.name = name
                self.grade = grade
                self.age = age
        def __repr__(self):
                return repr((self.name, self.grade, self.age))

student_objects = [
        Student('john', 'A', 15),
        Student('jane', 'B', 12),
        Student('dave', 'B', 10),
]

sorted(student_objects, key=attrgetter('age'))
#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
如果不使用operator module的話,也可以自己用lambda來寫一個key function
sorted(student_objects, key=lambda student: student.age)
#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
如果要先比成績再比年齡的話,用operator module很簡單就可以做到
sorted(student_objects, key=attrgetter('grade', 'age'))
#[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
在Python 2.x時候sorted還有提供cmp這個參數
不過在Python 3.0之後被拿掉了,使用Python 3.0的時候要注意不要用到舊的語法

1 則留言: