sorted與sort區別

使用sorted()排序值

開始使用Python排序,首先要了解如何對數字數據和字符串數據進行排序。

1. 排序數字型數據

可以使用Python通過sorted()對列表進行排序。比如定義了一個整數列表,然後使用numbers變量作爲參數調用sorted():

>>> numbers = [6, 9, 3, 1]
>>> sorted(numbers)
[1, 3, 6, 9]
>>> numbers
[6, 9, 3, 1]

輸出是一個新的排序列表,如果打印原始變量時,原始數字變量numbers未改變,因爲sorted()只提供已排序的輸出,而不會更改原始值。這意味着sorted()可以在列表中使用,將輸出立即分配給變量:

>>> numbers = [6, 9, 3, 1]
>>> numbers_sorted = sorted(numbers)
>>> numbers_sorted
[1, 3, 6, 9]
>>> numbers
[6, 9, 3, 1]

我們還可以通過調用sorted的help()來確認所有這些觀察結果。可選參數key和reverse將在本教程後面介紹:

>>> # Python 3
>>> help(sorted)
Help on built-in function sorted in module builtins:
sorted(iterable, /, *, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order.
A custom key function can be supplied to customize the sort order, and the
reverse flag can be set to request the result in descending order.

像操作列表一樣,sorted()也可同樣地用於元組和集合:

>>> numbers_tuple = (6, 9, 3, 1)
>>> numbers_set = {5, 5, 10, 1, 0}
>>> numbers_tuple_sorted = sorted(numbers_tuple)
>>> numbers_set_sorted = sorted(numbers_set)
>>> numbers_tuple_sorted
[1, 3, 6, 9]
>>> numbers_set_sorted
[0, 1, 5, 10]

注意到,即使輸入一個集合和一個元組,輸出也是一個列表,因爲sorted()按定義返回一個新列表。如果需要匹配輸入類型,則可以將返回的對象強制轉換爲新類型。如果嘗試將結果列表強制轉換回集合,結果將是無序的,因爲集合是無序的,如下:

>>> numbers_tuple = (6, 9, 3, 1)
>>> numbers_set = {5, 5, 10, 1, 0}
>>> numbers_tuple_sorted = sorted(numbers_tuple)
>>> numbers_set_sorted = sorted(numbers_set)
>>> numbers_tuple_sorted
[1, 3, 6, 9]
>>> numbers_set_sorted
[0, 1, 5, 10]
>>> tuple(numbers_tuple_sorted)
(1, 3, 6, 9)
>>> set(numbers_set_sorted)
{0, 1, 10, 5}

2. 排序字符串型數據

字符串類型與其他可迭代對象類似,如列表和元組。下面的示例顯示了sorted()如何將傳遞給它的字符串進行遍歷,並在輸出中對每個字符進行排序:

>>> string_number_value = '34521'
>>> string_value = 'I like to sort'
>>> sorted_string_number = sorted(string_number_value)
>>> sorted_string = sorted(string_value)
>>> sorted_string_number
['1', '2', '3', '4', '5']
>>> sorted_string
[' ', ' ', ' ', 'I', 'e', 'i', 'k', 'l', 'o', 'o', 'r', 's', 't', 't']

sorted()將字符串視爲列表並遍歷每個元素。在字符串中,每個元素表示字符串中的一個字符,sorted會以相同的方式處理一個字符串,對每個字符進行排序,包括空格。 .

我們通過使用split()可以改變輸出是單個字符的結果,以空格爲邊界將原始字符串拆分成幾個單詞,再通過.join()將幾個單詞重新組合在一起成爲新的字符串,具體如下:

>>> string_value = 'I like to sort'
>>> sorted_string = sorted(string_value.split())
>>> sorted_string
['I', 'like', 'sort', 'to']
>>> ' '.join(sorted_string)
'I like sort to'

Python排序的侷限性和陷阱

當使用Python對整數值進行排序時,可能會出現一些限制和奇怪的現象。

1. 具有不能比較數據類型的列表無法進行排序

有些數據類型使用sorted是無法進行比較的,因爲它們的類型不同。如果嘗試在包含不可比較數據的列表上使用sorted(),Python將返回錯誤。在此示例中,由於不兼容性,無法對同一列表中的None和int進行排序:

>>> mixed_types = [None, 0]
>>> sorted(mixed_types)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'NoneType'
此錯誤顯示了爲什麼Python無法對給定的值進行排序。
它試圖通過使用小於運算符(<)來確定值,以確定排序順序中哪個值較低。

例如,數字1應該出現在蘋果這個詞之前嗎?但是,如果迭代器包含所有數字的整數和字符串的組合,則可以使用列表推導將它們強制轉換爲可比較的數據類型:

>>> mixed_numbers = [5, "1", 100, "34"]
>>> sorted(mixed_numbers)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'int'
>>> # List comprehension to convert all values to integers
>>> [int(x) for x in mixed_numbers]
[5, 1, 100, 34]
>>> sorted([int(x) for x in mixed_numbers])
[1, 5, 34, 100]

mixed_numbers中的每個元素都調用了int()來將任何str值轉換爲int值。然後調用sorted()併成功比較每個元素並提供排序的輸出。

另外,Python還可以隱式地將值轉換爲另一種類型。在下面的示例中,1 <= 0的評估是false語句,因此評估的輸出將爲False。數字1可以轉換爲True作爲bool類型,而0轉換爲False。

即使列表中的元素看起來不同,它們也可以全部轉換爲布爾值(True或False)並使用sorted()進行相互比較:

>>> similar_values = [False, 0, 1, 'A' == 'B', 1 <= 0]
>>> sorted(similar_values)
[False, 0, False, False, 1]

‘A’=='B’和1 <= 0轉換爲False並在有序輸出中返回。

此示例說明了排序的一個重要方面:排序穩定性。在Python中,當你對相等的值進行排序時,它們將在輸出中保留其原始順序。即使1移動,所有其他值都相等,它們保持相對於彼此的原始順序。在下面的示例中,所有值都被視爲相等,並將保留其原始位置:

>>> false_values = [False, 0, 0, 1 == 2, 0, False, False]
>>> sorted(false_values)
[False, 0, 0, False, 0, False, False]

如果檢查原始順序和排序輸出,可以看到1 == 2轉換爲False,所有排序輸出都是原始順序。

2. 當排序字符串時,大小寫很重要

sorted()可用於字符串列表,以按升序對值進行排序,默認情況下按字母順序排列:

>>> names = ['Harry', 'Suzy', 'Al', 'Mark']
>>> sorted(names)
['Al', 'Harry', 'Mark', 'Suzy']

但是,Python使用每個字符串中第一個字母的Unicode代碼點來確定升序排序。意思是sorted()不會將名稱Al和al視爲相同。此示例使用ord()返回每個字符串中第一個字母的Unicode代碼點:

>>> names_with_case = ['harry', 'Suzy', 'al', 'Mark']
>>> sorted(names_with_case)
['Mark', 'Suzy', 'al', 'harry']
>>> # 每個word中第一個字母的unicode代碼點列表推導式
>>> [(ord(name[0]), name[0]) for name in sorted(names_with_case)]
[(77, 'M'), (83, 'S'), (97, 'a'), (104, 'h')]

name [0]返回sorted(names_with_case)的每個元素中的第一個字符,ord()提供Unicode代碼點。即使a在字母表中的M之前,M的代碼點在a之前,因此排序的輸出首先是M。

如果第一個字母相同,則sorted()將使用第二個字符來確定順序,第三個字符等,依此類推,一直到字符串的結尾:

>>> very_similar_strs = ['hhhhhd', 'hhhhha', 'hhhhhc','hhhhhb']
>>> sorted(very_similar_strs)
['hhhhha', 'hhhhhb', 'hhhhhc', 'hhhhhd']

除最後一個字符外,very_similar_strs的每個值都相同。 sorted()比較字符串,因爲前五個字符相同,輸出將基於第六個字符。

包含相同值的字符串將最終排序爲最短到最長,因爲較短的字符串沒有要與較長字符串進行比較的元素:

>>> different_lengths = ['hhhh', 'hh', 'hhhhh','h']
>>> sorted(different_lengths)
['h', 'hh', 'hhhh', 'hhhhh']

最短的字符串h排序第一,最長的字符串hhhhh排序最後。

用reverse參數使用sorted()

如sorted()的help()文檔所示,有一個名爲reverse的可選關鍵字參數,它將根據分配給它的布爾值更改排序行爲。如果將reverse指定爲True,則排序將按降序排列:

>>> names = ['Harry', 'Suzy', 'Al', 'Mark']
>>> sorted(names)
['Al', 'Harry', 'Mark', 'Suzy']
>>> sorted(names, reverse=True)
['Suzy', 'Mark', 'Harry', 'Al']

排序邏輯保持不變,這意味着名稱仍按其第一個字母排序。但是,如果reverse關鍵字設置爲True,則輸出反轉。

如果指定了False,則排序將保持升序。可以使用前面的任何示例來使用True或False來查看reverse的行爲:

>>> names_with_case = ['harry', 'Suzy', 'al', 'Mark']
>>> sorted(names_with_case, reverse=True)
['harry', 'al', 'Suzy', 'Mark']
>>> similar_values = [False, 1, 'A' == 'B', 1 <= 0]
>>> sorted(similar_values, reverse=True)
[1, False, False, False]
>>> numbers = [6, 9, 3, 1]
>>> sorted(numbers, reverse=False)
[1, 3, 6, 9]

sorted()使用key參數排序

sorted()最強大的功能之一是一個叫做key的關鍵字參數。此參數需要將函數傳遞給它,並且該函數將用於要排序的列表中的每個值,以確定生成的順序。

我們假設排序一個特定列表的要求是列表中字符串的長度,最短到最長。返回字符串長度len()的函數將與key參數一起使用:

>>> word = 'paper'
>>> len(word)
5
>>> words = ['banana', 'pie', 'Washington', 'book']
>>> sorted(words, key=len)
['pie', 'book', 'banana', 'Washington']

生成的順序是一個字符串順序最短到最長的列表。列表中每個元素的長度由len確定,然後以升序返回。

回到前面的例子,當大小寫不同時按第一個字母排序。key可以通過將整個字符串轉換爲小寫來解決該問題:

>>> names_with_case = ['harry', 'Suzy', 'al', 'Mark']
>>> sorted(names_with_case)
['Mark', 'Suzy', 'al', 'harry']
>>> sorted(names_with_case, key=str.lower)
['al', 'harry', 'Mark', 'Suzy']

輸出值尚未轉換爲小寫,因爲key不會處理原始列表中的數據。在排序期間,傳遞給key的函數將在每個元素上調用以確定排序順序,但原始值仍將體現在輸出中。使用帶有key參數的函數時,有兩個主要限制。

首先,傳遞給key的函數中參數的數量必須爲1。

下面的示例顯示了帶有兩個參數的加法函數的定義。當該函數用於數字列表中的鍵時,它會失敗,因爲它缺少第二個參數。每次在排序期間調用add()時,它一次只從列表中接收一個元素:

>>> def add(x, y):
... return x + y
...
>>> values_to_add = [1, 2, 3]
>>> sorted(values_to_add, key=add)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: add() missing 1 required positional argument: 'y'

第二個限制是與key一起使用的函數必須能夠處理iterable中的所有值。

例如,有一個數字列表,表示爲要在sorted中使用的字符串,而key將嘗試將它們轉換爲使用int。如果iterable中的值不能轉換爲整數,則該函數將失敗:

>>> values_to_cast = ['1', '2', '3', 'four']
>>> sorted(values_to_cast, key=int)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'four'

作爲字符串的每個數值都可以轉換爲int,但是four不能。這會導致引發ValueError並解釋four無法轉換爲int,因爲它無效。

key功能非常強大,因爲幾乎任何內置或用戶定義的函數都可用於操作輸出順序。

如果排序要求是按每個字符串中的最後一個字母排序可迭代(如果字母相同,然後使用下一個字母),則可以定義函數,然後在排序中使用。下面的示例定義了一個函數,該函數反轉傳遞給它的字符串,然後該函數用作鍵的參數:

>>> def reverse_word(word):
... return word[::-1]
...
>>> words = ['banana', 'pie', 'Washington', 'book']
>>> sorted(words, key=reverse_word)
['banana', 'pie', 'book', 'Washington']

word[::-1]切片語法用於反轉字符串。每個元素都會應用reverse_word(),排序順序將基於後向單詞中的字符。

當然,也可以使用key參數中定義的lambda函數,而不是編寫獨立函數。 lambda匿名函數:1)必須內聯定義;2)沒有名字;3)不能包含statement;4)像函數一樣執行。

在下面的示例中,key被定義爲沒有名稱的lambda,lambda採用的參數是x,然後x [:: -1]是將對參數執行的操作:

>>> words = ['banana', 'pie', 'Washington', 'book']
>>> sorted(words, key=lambda x: x[::-1])
['banana', 'pie', 'book', 'Washington']

在每個元素上調用x [::-1]並反轉該單詞。然後將反轉的輸出用於排序,但仍返回原始單詞。 如果需求發生變化,要求順序也應該反轉,那麼reverse關鍵字可以與key參數一起使用:

>>> words = ['banana', 'pie', 'Washington', 'book']
>>> sorted(words, key=lambda x: x[::-1], reverse=True)
['Washington', 'book', 'pie', 'banana']

當需要基於屬性對類對象進行排序時,lambda函數也很有用。如果有一組學生並需要按最終成績(從最高到最低)對其進行排序,則可以使用lambda從該課程中獲取成績屬性:

>>> from collections import namedtuple
>>> StudentFinal = namedtuple('StudentFinal', 'name grade')
>>> bill = StudentFinal('Bill', 90)
>>> patty = StudentFinal('Patty', 94)
>>> bart = StudentFinal('Bart', 89)
>>> students = [bill, patty, bart]
>>> sorted(students, key=lambda x: getattr(x, 'grade'), reverse=True)
[StudentFinal(name='Patty', grade=94), StudentFinal(name='Bill', grade=90),
StudentFinal(name='Bart',
grade=89)]

此示例使用namedtuple生成具有name和grade屬性的類。 lambda在每個元素上調用getattr()並返回grade的值。 reverse設置爲True以使升序輸出轉爲降序,以便首先排序最高等級。

當在sorted()上同時使用key和reverse關鍵字參數時,如何進行排序的可能性是無窮無盡的。當對一個小函數使用基本lambda時,代碼可以保持乾淨和簡短,或者可以編寫一個全新的函數導入,並在key參數中使用它。

使用.sort()排序值

名稱相似的.sort()與sorted()內置函數有着很大的不同。雖然它們或多或少都可以完成相同的事情,但list.sort()的help()文檔突出顯示了.sort()和sorted()之間最重要的兩個區別:

>>> # Python2
Help on method_descriptor:
sort(...)
L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;
cmp(x, y) -> -1, 0, 1
>>> # Python3
>>> help(list.sort)
Help on method_descriptor:
sort(...)
L.sort(key=None, reverse=False) -> None -- stable sort *IN PLACE*

首先,sort是列表類的一種方法,只能與列表一起使用。它不是傳遞給它的迭代的內置函數。 其次,sort返回None並修改值。我們來看看代碼中這兩種差異的影響:

>>> values_to_sort = [5, 2, 6, 1]
>>> # 嘗試調用像使用sorted()調用sort()
>>> sort(values_to_sort)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'sort' is not defined
>>> # 嘗試在一個元組上使用 .sort()
>>> tuple_val = (5, 1, 3, 5)
>>> tuple_val.sort()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'sort'
>>> # 排序列表並且賦值給新的變量
>>> sorted_values = values_to_sort.sort()
>>> print(sorted_values)
None
>>> # 打印原始變量
>>> print(values_to_sort)
[1, 2, 5, 6]

與此代碼示例中的sorted()相比,sort()操作的方式有一些非常顯着的差異:

1.sort()不產生有序輸出,因此對新變量的賦值僅傳遞None類型。
2.values_to_sort列表已就地更改,並且不以任何方式維持原始順序。

這些差異使得.sort()和sorted()絕對不能在代碼中互換,如果以錯誤的方式使用它們,它們會產生意想不到的結果。

.sort()具有相同的key和reverse可選關鍵字參數,這些參數產生與sorted()相同的強大功能。在這裏,可以按第三個單詞的第二個字母對短語列表進行排序,然後反向返回列表:

>>> phrases = ['when in rome',
... 'what goes around comes around',
... 'all is fair in love and war'
... ]
>>> phrases.sort(key=lambda x: x.split()[2][1], reverse=True)
>>> phrases
['what goes around comes around', 'when in rome', 'all is fair in love and war']
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章