sort( )について少し詳しく
デフォルトでは、文字列なら文字コード順、数値なら値の小さい順に並び替えます。リストやタプルの場合は、0番目の要素で並び替えを行います。
>>> list = [94, 25, 9, 67] >>> list.sort() >>> list [9, 25, 67, 94] >>> >>> list = ["banana", "grape", "orange", "apple"] >>> list.sort() >>> list ['apple', 'banana', 'grape', 'orange'] o>>> >>> list = [(3, 99), (4, 95), (1, 123), (2, 14)] >>> list.sort() >>> list [(1, 123), (2, 14), (3, 99), (4, 95)]
cmpキーワード
>>> list = ["98", "101", "100", "99"] >>> list.sort() >>> list ['100', '101', '98', '99']
数値文字列はあくまで文字列ですので、"101"は"99"より先に来ます。
数値文字列を数値としての大小で並び替えるには、sort( )の第一引数もしくはcmpキーワードに比較関数を指定します。比較関数は二つの引数を比較した結果に応じて、負の数、0、正の数のいずれかを返す必要があります。sort( )は先頭から順に二つの要素を比較関数に渡し、その結果が正の数であれば、それらの要素の位置を入れ替えます。
>>> list = ["98", "101", "100", "99"] >>> list.sort(cmp=lambda x,y: cmp(int(x), int(y))) >>> list ['98', '99', '100', '101']
cmp( )はaとbの二つの引数を取り、a < bなら-1、a == bなら0、a > bなら1を返します。ちなみにデフォルトの動作はsort(cmp=cmp)と同じです。
keyキーワード
keyキーワードを使って同じことができます。keyにはリストの各要素から比較関数に渡すキーを取り出すための関数を指定します。
>>> list = ["98", "101", "100", "99"] >>> list.sort(key=int) >>> list ['98', '99', '100', '101']
cmpの場合は比較関数の中で各要素を数値へ変換しますが、keyの場合は比較関数に渡す前に数値へ変換します。
降順に並び替える
reverseにTrueを指定します。
list = [94, 25, 9, 67] >>> list.sort(reverse=True) >>> list [94, 67, 25, 9] >>> >>> list = ["banana", "grape", "orange", "apple"] >>> list.sort(reverse=True) >>> list ['orange', 'grape', 'banana', 'apple']
cmpとkeyの比較
同じように動作するなら、keyを指定した方が速いです。
# cmp_sort.py for i in xrange(10000): list = ["99", "129", "92", "116", "43", "75", "219", "8", "68"] list.sort(cmp=lambda x,y: cmp(int(x), int(y))) print list
# key_sort.py for i in xrange(10000): list = ["99", "129", "92", "116", "43", "75", "219", "8", "68"] list.sort(key=int) print list
$ time python cmp_sort.py ['8', '43', '68', '75', '92', '99', '116', '129', '219'] real 0m8.624s user 0m0.000s sys 0m0.000s $ $ time python key_sort.py ['8', '43', '68', '75', '92', '99', '116', '129', '219'] real 0m2.278s user 0m0.000s sys 0m0.000s
これはint( )の呼び出し回数に差があるからです。key関数で各要素からキーを取り出すのは一度だけですが、比較関数の中で呼び出されるint( )は当然、各要素につき複数回呼び出されることになります。
count = 0 def _int(str): global count count += 1 return int(str) cmp_list = key_list = \ ["99", "129", "92", "116", "43", "75", "219", "8", "68"] cmp_list.sort(cmp=lambda x,y: cmp(_int(x), _int(y))) cmp_count = count count = 0 key_list.sort(key=_int) key_count = count print "cmp_list =", cmp_list print "key_list =", key_list print "cmp_count =", cmp_count print "key_count =", key_count
cmp_list = ['8', '43', '68', '75', '92', '99', '116', '129', '219'] key_list = ['8', '43', '68', '75', '92', '99', '116', '129', '219'] cmp_count = 40 key_count = 9
降順に並び替える場合、reverse=Trueを指定するかどうかで、どれぐらいの差が出るのかも調べてみます。
# key_sort.py for i in xrange(10000): list = ["99", "129", "92", "116", "43", "75", "219", "8", "68"] list.sort(key=lambda x: -int(x))
# key-reverse_sort.py for i in xrange(10000): list = ["99", "129", "92", "116", "43", "75", "219", "8", "68"] list.sort(key=int, reverse=True)
$ time python key_sort.py real 0m2.931s user 0m0.000s sys 0m0.000s $ $ time python key-reverse_sort.py real 0m2.228s user 0m0.000s sys 0m0.000s
cmpとkeyほどではないですが、reverse=Tureを指定した方が速いようです。