Bài 6.1 - Hàm sort có sẵn của Python

Bài 6.1 - Hàm sort có sẵn của Python
Chào mừng các bạn đến với bài viết sâu hơn về một trong những thao tác cực kỳ phổ biến và quan trọng trong lập trình: sắp xếp dữ liệu. Dù bạn đang làm việc với danh sách các con số, tên, hay các đối tượng phức tạp hơn, việc đưa chúng vào một trật tự nhất định là điều thường xuyên phải làm.
May mắn thay, Python cung cấp cho chúng ta hai công cụ mạnh mẽ và có sẵn để giải quyết bài toán sắp xếp một cách hiệu quả:
- Phương thức
list.sort()
: Dành riêng cho các đối tượng danh sách (list). - Hàm
sorted()
: Hoạt động trên mọi loại đối tượng iterable (có thể lặp qua được) và trả về một danh sách mới đã được sắp xếp.
Chúng ta hãy cùng đi sâu vào cách sử dụng từng công cụ này nhé!
1. Phương thức list.sort()
Phương thức list.sort()
là một phương thức của đối tượng list. Điều này có nghĩa là bạn chỉ có thể gọi nó trên một danh sách.
Điểm quan trọng nhất cần ghi nhớ về list.sort()
là nó sẽ sắp xếp danh sách ngay tại chỗ (in-place). Điều này có nghĩa là nó thay đổi trực tiếp danh sách ban đầu và không tạo ra danh sách mới.
Sắp xếp cơ bản (Tăng dần)
Mặc định, list.sort()
sẽ sắp xếp các phần tử theo thứ tự tăng dần (ascending) đối với số và theo thứ tự từ điển (alphabetical) đối với chuỗi.
Ví dụ 1: Sắp xếp danh sách số
# Danh sách gốc
my_numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print("Danh sách gốc:", my_numbers)
# Gọi phương thức sort()
my_numbers.sort()
# In danh sách sau khi sắp xếp (đã bị thay đổi)
print("Danh sách sau khi sort():", my_numbers)
Giải thích code:
- Ban đầu, chúng ta có danh sách
my_numbers
theo thứ tự ngẫu nhiên. - Khi gọi
my_numbers.sort()
, Python sẽ sắp xếp các phần tử bên trongmy_numbers
theo thứ tự tăng dần. - Lưu ý rằng chúng ta không gán kết quả của
my_numbers.sort()
cho biến nào cả, bởi vì nó thay đổi danh sách gốc trực tiếp. Dòngprint("Danh sách sau khi sort():", my_numbers)
in ra chính danh sáchmy_numbers
sau khi đã được sắp xếp.
Ví dụ 2: Sắp xếp danh sách chuỗi
# Danh sách gốc
my_words = ["banana", "apple", "cherry", "date"]
print("Danh sách gốc:", my_words)
# Gọi phương thức sort()
my_words.sort()
# In danh sách sau khi sắp xếp
print("Danh sách sau khi sort():", my_words)
Giải thích code:
- Tương tự như số,
list.sort()
sắp xếp các chuỗi dựa trên thứ tự từ điển (alphabetical). - "apple" đứng trước "banana", "banana" đứng trước "cherry", v.v. Danh sách my_words được thay đổi thành
['apple', 'banana', 'cherry', 'date']
.
Sắp xếp giảm dần với reverse=True
Để sắp xếp theo thứ tự giảm dần (descending), bạn chỉ cần thêm tham số reverse=True
khi gọi phương thức sort()
.
Ví dụ 3: Sắp xếp giảm dần
my_numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print("Danh sách gốc:", my_numbers)
# Sắp xếp giảm dần
my_numbers.sort(reverse=True)
print("Danh sách sau khi sort(reverse=True):", my_numbers)
Giải thích code:
- Việc thêm
reverse=True
vào lệnh gọisort()
đảo ngược thứ tự sắp xếp mặc định. - Danh sách
my_numbers
giờ đây được sắp xếp từ số lớn nhất đến số nhỏ nhất:[9, 6, 5, 4, 3, 2, 1, 1]
.
Sắp xếp theo tiêu chí tùy chỉnh với key
Tham số key
là một tính năng rất mạnh mẽ cho phép bạn xác định một tiêu chí hoặc một hàm để sắp xếp. Thay vì so sánh trực tiếp các phần tử, Python sẽ gọi hàm được cung cấp trong key
cho mỗi phần tử, và sau đó sử dụng giá trị trả về của hàm đó để so sánh và sắp xếp.
Ví dụ 4: Sắp xếp chuỗi theo độ dài
my_words = ["banana", "apple", "cherry", "date"]
print("Danh sách gốc:", my_words)
# Sắp xếp theo độ dài của chuỗi
my_words.sort(key=len)
print("Danh sách sau khi sort(key=len):", my_words)
# Sắp xếp theo độ dài giảm dần
my_words.sort(key=len, reverse=True)
print("Danh sách sau khi sort(key=len, reverse=True):", my_words)
Giải thích code:
- Ở đây, chúng ta sử dụng hàm
len
(hàm trả về độ dài của một đối tượng) làm giá trị cho tham sốkey
. - Thay vì sắp xếp theo thứ tự từ điển (
"apple"
,"banana"
), Python sẽ sắp xếp dựa trên kết quả củalen()
cho mỗi từ. Độ dài của các từ là: "banana" (6), "apple" (5), "cherry" (6), "date" (4). - Khi sắp xếp tăng dần theo độ dài, kết quả là
['date', 'apple', 'banana', 'cherry']
. Lưu ý rằng "banana" và "cherry" có cùng độ dài (6), thứ tự tương đối giữa chúng trong danh sách gốc được giữ nguyên (gọi là sắp xếp ổn định - stable sort). - Kết hợp
key=len
vớireverse=True
sẽ sắp xếp theo độ dài giảm dần.
Ví dụ 5: Sắp xếp danh sách các tuple theo phần tử cụ thể
Thường chúng ta có danh sách các bản ghi (có thể là tuple hoặc dictionary), và muốn sắp xếp chúng dựa trên một trường cụ thể.
students = [('Alice', 'A', 15), ('Bob', 'B', 12), ('Charlie', 'B', 16)]
print("Danh sách gốc:", students)
# Sắp xếp theo tên (phần tử thứ 0 trong mỗi tuple)
students.sort(key=lambda student: student[0])
print("Sắp xếp theo tên:", students)
# Sắp xếp theo tuổi (phần tử thứ 2 trong mỗi tuple)
students.sort(key=lambda student: student[2])
print("Sắp xếp theo tuổi:", students)
# Sắp xếp theo điểm (phần tử thứ 1), nếu điểm bằng nhau thì sắp xếp theo tuổi (phần tử thứ 2)
students.sort(key=lambda student: (student[1], student[2]))
print("Sắp xếp theo điểm và tuổi:", students)
Giải thích code:
- Chúng ta sử dụng hàm lambda (một loại hàm ẩn danh nhỏ) để tạo ra hàm tức thời cho tham số
key
. lambda student: student[0]
nghĩa là "đối với mỗistudent
(là một tuple), hãy trả về phần tử ở chỉ số 0". Python sẽ sử dụng giá trị này (tên) để sắp xếp.lambda student: student[2]
sử dụng phần tử ở chỉ số 2 (tuổi) làm khóa sắp xếp.lambda student: (student[1], student[2])
là một kỹ thuật rất hữu ích. Khikey
trả về một tuple, Python sẽ so sánh các tuple đó từng phần tử một từ trái sang phải. Đầu tiên, nó so sánh phần tử ở chỉ số 1 (điểm). Nếu hai sinh viên có cùng điểm ('B'), nó sẽ chuyển sang so sánh phần tử ở chỉ số 2 (tuổi) để phá vỡ sự đồng hạng. Kết quả là các sinh viên được sắp xếp trước hết theo điểm, sau đó theo tuổi trong nhóm cùng điểm.
Lưu ý: list.sort()
trả về None
Một lỗi phổ biến mà người mới học hay mắc phải là mong đợi list.sort()
trả về danh sách đã sắp xếp. Nó không làm vậy! Nó trả về giá trị đặc biệt None
.
my_list = [5, 2, 8]
sorted_list_var = my_list.sort() # SAI LẦM!
print(sorted_list_var)
print(my_list) # Danh sách gốc đã bị thay đổi, nhưng biến mới là None
Giải thích code:
- Dòng
my_list.sort()
thực hiện việc sắp xếp trên my_list thành[2, 5, 8]
. - Tuy nhiên, giá trị mà
my_list.sort()
trả về làNone
. - Do đó, biến
sorted_list_var
nhận giá trị làNone
, chứ không phải danh sách đã sắp xếp. Hãy luôn gọilist.sort()
trên một dòng riêng biệt và sử dụng lại biến danh sách ban đầu sau đó.
2. Hàm sorted()
Khác với list.sort()
chỉ dành cho danh sách, sorted()
là một hàm có sẵn của Python (built-in function) và nó hoạt động trên mọi loại đối tượng iterable.
Điểm mấu chốt của sorted()
là nó luôn luôn trả về một danh sách mới đã được sắp xếp. Đối tượng iterable ban đầu không bị thay đổi.
Cú pháp cơ bản: sorted(iterable, *, key=None, reverse=False)
Sắp xếp các loại iterable khác nhau
Ví dụ 6: Sắp xếp Tuple
my_tuple = (3, 1, 4, 1, 5, 9)
print("Tuple gốc:", my_tuple)
# Sử dụng sorted()
sorted_list_from_tuple = sorted(my_tuple)
print("Danh sách mới từ tuple:", sorted_list_from_tuple)
print("Tuple gốc sau khi sorted():", my_tuple) # Tuple gốc không đổi
Giải thích code:
- Hàm
sorted()
nhậnmy_tuple
làm đối số. - Nó xử lý các phần tử trong tuple và trả về một danh sách mới
[1, 1, 3, 4, 5, 9]
đã được sắp xếp. - Tuple gốc
my_tuple
vẫn giữ nguyên giá trị ban đầu vì tuple là đối tượng bất biến (immutable).
Ví dụ 7: Sắp xếp Chuỗi
my_string = "python"
print("Chuỗi gốc:", my_string)
# Sử dụng sorted()
sorted_list_from_string = sorted(my_string)
print("Danh sách các ký tự đã sắp xếp:", sorted_list_from_string)
print("Chuỗi gốc sau khi sorted():", my_string) # Chuỗi gốc không đổi
Giải thích code:
- Khi áp dụng
sorted()
cho một chuỗi, nó coi chuỗi như một chuỗi các ký tự lặp lại được. - Hàm trả về một danh sách mới chứa các ký tự của chuỗi được sắp xếp theo thứ tự từ điển:
['h', 'n', 'o', 'p', 't', 'y']
. Chuỗi gốc "python" không thay đổi.
Ví dụ 8: Sắp xếp các Khóa trong Dictionary
my_dict = {'apple': 3, 'banana': 1, 'cherry': 2}
print("Từ điển gốc:", my_dict)
# Sử dụng sorted()
sorted_keys = sorted(my_dict)
print("Danh sách các khóa đã sắp xếp:", sorted_keys)
print("Từ điển gốc sau khi sorted():", my_dict)
Giải thích code:
- Khi gọi
sorted()
trên một dictionary, mặc định nó sẽ hoạt động trên các khóa của dictionary. - Hàm trả về một danh sách mới chứa các khóa của từ điển được sắp xếp theo thứ tự từ điển:
['apple', 'banana', 'cherry']
. Từ điển gốc không bị thay đổi thứ tự bên trong (mặc dù từ Python 3.7, dictionary giữ thứ tự chèn, nhưngsorted()
vẫn trả về danh sách khóa).
Tham số reverse
và key
trong sorted()
Hàm sorted()
cũng chấp nhận các tham số reverse
và key
hoạt động tương tự như với phương thức list.sort()
.
Ví dụ 9: Sử dụng reverse=True
với sorted()
my_numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print("Iterable gốc:", my_numbers)
# Sắp xếp giảm dần với sorted()
sorted_desc = sorted(my_numbers, reverse=True)
print("Danh sách giảm dần mới:", sorted_desc)
print("Iterable gốc sau khi sorted():", my_numbers)
Giải thích code:
- Tham số
reverse=True
làm chosorted()
trả về một danh sách được sắp xếp theo thứ tự giảm dần. - Kết quả là
[9, 6, 5, 4, 3, 2, 1, 1]
, nhưng danh sáchmy_numbers
ban đầu vẫn là[3, 1, 4, 1, 5, 9, 2, 6]
.
Ví dụ 10: Sử dụng key
với sorted()
Các ví dụ về key
với list.sort()
(sắp xếp chuỗi theo độ dài, sắp xếp tuple theo phần tử) cũng hoạt động y hệt với sorted()
, chỉ khác là sorted()
sẽ trả về một danh sách mới thay vì thay đổi tại chỗ.
my_words = ["banana", "apple", "cherry", "date"]
print("Iterable gốc:", my_words)
# Sắp xếp theo độ dài của chuỗi với sorted()
sorted_by_len = sorted(my_words, key=len)
print("Danh sách mới sắp xếp theo độ dài:", sorted_by_len)
print("Iterable gốc sau khi sorted():", my_words)
students = [('Alice', 'A', 15), ('Bob', 'B', 12), ('Charlie', 'B', 16)]
print("\nIterable gốc:", students)
# Sắp xếp theo tuổi với sorted()
sorted_by_age = sorted(students, key=lambda student: student[2])
print("Danh sách mới sắp xếp theo tuổi:", sorted_by_age)
print("Iterable gốc sau khi sorted():", students)
Giải thích code:
- Hàm
sorted()
sử dụng tham sốkey
tương tự nhưlist.sort()
để xác định tiêu chí sắp xếp tùy chỉnh. - Sự khác biệt chính là nó luôn trả về một danh sách mới. Danh sách
my_words
vàstudents
gốc không hề bị thay đổi thứ tự.
3. Khi nào sử dụng list.sort()
và khi nào sử dụng sorted()
?
Việc lựa chọn giữa hai công cụ này phụ thuộc vào nhu cầu của bạn:
- Sử dụng
list.sort()
khi:- Bạn đang làm việc với một đối tượng list (danh sách).
- Bạn không cần giữ lại thứ tự ban đầu của danh sách và muốn tiết kiệm bộ nhớ bằng cách thực hiện sắp xếp ngay tại chỗ. Đây thường là lựa chọn hiệu quả hơn về mặt bộ nhớ cho danh sách lớn.
- Sử dụng
sorted()
khi:- Bạn muốn sắp xếp bất kỳ đối tượng iterable nào (tuple, chuỗi, set, dictionary, generator, v.v.).
- Bạn cần nhận được một danh sách mới đã sắp xếp mà không làm thay đổi đối tượng gốc. Điều này rất hữu ích khi bạn cần giữ lại dữ liệu ban đầu.
Hiểu rõ sự khác biệt này sẽ giúp bạn chọn đúng công cụ và viết code Python hiệu quả hơn!
Comments