Bỏ qua đến nội dung chính
Học Python
Logo Python cạnh cửa sổ code nối với biểu đồ cột xếp tăng dần

sort và sorted trong Python: sắp xếp list, dict

AIDev TeamTham vấn chuyên môn: Nguyễn Việt Chung

Đăng 10 phút đọc

Mục lục · 21

sort và sorted trong Python đều sắp xếp tăng dần, khác nhau ở chỗ list.sort() sửa thẳng list và trả về None, còn sorted() tạo list mới từ bất kỳ dãy nào. Bài này đi qua key, reverse, sắp xếp dict, xếp tên tiếng Việt cho đúng, kèm lỗi thật và số đo trên Python 3.15.1.

Muốn sắp xếp trong Python, bạn có hai cách: phương thức list.sort() sửa thẳng list đang có, còn hàm sorted() để nguyên dữ liệu gốc và trả về một list mới đã xếp. Hai cách nhận cùng tham số key và reverse, cùng thuật toán, cùng tốc độ. Chọn cách nào phụ thuộc vào việc bạn còn cần thứ tự cũ hay không.

Ngày 26/10/2026, mình chạy mọi ví dụ trong bài trên Python 3.15.1, máy CPU thuê theo giờ của AIDev Cloud chạy Ubuntu 24.04. Mấy điều nên nắm trước:

  • ds.sort() chỉ dùng được với list và trả về None. Viết ds = ds.sort() là mất luôn list.

  • sorted() nhận mọi thứ lặp được (list, tuple, chuỗi, dict, set) và luôn trả về list.

  • key= nhận một hàm, Python gọi hàm đó cho từng phần tử rồi xếp theo kết quả.

  • Mặc định Python xếp chuỗi theo mã Unicode, nên tên tiếng Việt có dấu bị xếp sai. Bài có cách sửa ở mục riêng.

sort và sorted trong Python khác nhau thế nào?

Ví dụ dưới xếp một list điểm bằng cả hai cách:

python
diem = [7.5, 9, 6, 8.25]

moi = sorted(diem)
print("sorted():", moi, "| list gốc:", diem)

kq = diem.sort()
print("sort():  ", diem, "| giá trị trả về:", kq)
Terminal Python 3.15.1 trên AIDev Cloud: sorted() trả về 6, 7.5, 8.25, 9 trong khi list gốc vẫn là 7.5, 9, 6, 8.25; sort() xếp lại chính list diem và trả về None

Sau dòng sorted(diem), list diem vẫn giữ thứ tự cũ. Sau dòng diem.sort(), chính list diem đã đổi, còn giá trị trả về là None. Python cố ý thiết kế như vậy để bạn không nhầm rằng sort() tạo ra list mới.

list.sort()

sorted()

Dùng với

chỉ list

mọi thứ lặp được: list, tuple, chuỗi, dict, set

Dữ liệu gốc

bị sửa

giữ nguyên

Trả về

None

list mới

Bộ nhớ

không tạo list mới

tạo thêm một list cùng độ dài

Nên dùng khi

không cần thứ tự cũ nữa

cần giữ dữ liệu gốc, hoặc dữ liệu không phải list

Sắp xếp theo key và reverse ra sao?

reverse=True xếp giảm dần. key= nhận một hàm: Python gọi hàm đó với từng phần tử và xếp theo giá trị hàm trả về, còn phần tử thì giữ nguyên. Truyền key=len là xếp theo độ dài:

python
mon = ["phở", "bún chả", "cơm tấm", "bánh xèo", "chè"]

print(sorted(mon, key=len))
print(sorted(mon, key=len, reverse=True))

Khi cần một phép tính riêng, bạn viết hàm lambda (hàm không tên viết trong một dòng). Ví dụ lambda x: x[1] nghĩa là "lấy phần tử thứ hai của x".

Sắp xếp dict theo giá trị

Dict không có phương thức sort(). sorted(gia) chỉ trả về các khóa đã xếp. Muốn xếp theo giá trị, bạn xếp các cặp (khóa, giá trị) lấy từ gia.items(), rồi bọc lại bằng dict() nếu cần dict:

python
gia = {"phở": 55000, "bún chả": 45000, "cơm tấm": 40000, "bánh xèo": 60000}
print(sorted(gia))                                     # chỉ sắp xếp khóa
print(sorted(gia.items(), key=lambda x: x[1]))         # theo giá, rẻ trước
print(dict(sorted(gia.items(), key=lambda x: x[1], reverse=True)))
Terminal Python 3.15.1: xếp món theo độ dài phở, chè, bún chả, cơm tấm, bánh xèo và ngược lại; sorted dict chỉ ra khóa; xếp items theo giá cơm tấm 40000 đến bánh xèo 60000; dict mới theo giá giảm dần

Ở dòng đầu của kết quả, "phở" và "chè" cùng dài 3 ký tự, "bún chả" và "cơm tấm" cùng dài 7, và mỗi cặp giữ đúng thứ tự như trong list gốc. Tính chất này gọi là tính ổn định, mục sắp xếp theo nhiều tiêu chí nói kỹ hơn. Từ Python 3.7, dict giữ thứ tự thêm vào, nên dict tạo từ list đã xếp sẽ giữ thứ tự đó.

Sắp xếp theo nhiều tiêu chí như thế nào?

Cho key trả về một tuple (nhiều giá trị đặt trong ngoặc tròn, như ("B", -8.5)). Python so phần tử đầu của tuple trước, bằng nhau mới so tới phần tử sau. Muốn một tiêu chí tăng, tiêu chí kia giảm, với số thì đặt dấu trừ trước tiêu chí giảm:

python
hoc_vien = [
    {"ten": "Lan", "lop": "B", "diem": 8.5},
    {"ten": "Minh", "lop": "A", "diem": 7.0},
    {"ten": "An", "lop": "B", "diem": 9.0},
    {"ten": "Hòa", "lop": "A", "diem": 8.5},
]

for hv in sorted(hoc_vien, key=lambda h: (h["lop"], -h["diem"])):
    print(hv["lop"], hv["ten"], hv["diem"])

Dòng key này xếp theo lớp tăng dần, trong cùng lớp thì điểm cao trước. Kết quả ở nửa trên của ảnh dưới.

Nửa dưới của ảnh là ví dụ về tính ổn định. Tài liệu Python ghi rõ mọi phép sắp xếp đều ổn định: các phần tử có cùng khóa giữ nguyên thứ tự ban đầu. Mình xếp năm đơn hàng theo tỉnh, hai đơn Hà Nội vẫn đứng theo thứ tự DH01 rồi DH03 như lúc chưa xếp:

Terminal Python 3.15.1: nhieu_tieu_chi.py in lớp A Hòa 8.5, A Minh 7.0, B An 9.0, B Lan 8.5; on_dinh.py in Cần Thơ DH04, Hà Nội DH01, Hà Nội DH03, Đà Nẵng DH02, Đà Nẵng DH05

Trong ảnh còn thấy một lỗi: Đà Nẵng đứng sau Hà Nội. Theo bảng chữ cái tiếng Việt, Đ phải đứng trước H.

Sắp xếp tên tiếng Việt có dấu thế nào cho đúng?

Python so chuỗi theo mã Unicode của từng ký tự. Chữ không dấu A đến Z có mã nhỏ, còn Á, Â, Đ, Ơ có mã lớn hơn, nên mọi tên bắt đầu bằng chữ có dấu bị đẩy xuống cuối. Mình thử với 12 tên, lần đầu chạy trên máy mới còn gặp thêm lỗi locale (bộ quy tắc ngôn ngữ của hệ điều hành) chưa được cài:

Terminal Python 3.15.1: sorted() xếp An, Anh, Bình, Châu, Dũng, Oanh rồi mới tới Ánh, Ân, Đức, Ơn, Ạnh, Ảnh; dòng setlocale vi_VN.UTF-8 báo locale.Error unsupported locale setting
text
locale.Error: unsupported locale setting

Cách 1: dùng locale vi_VN của hệ điều hành

Trên Ubuntu, bạn sinh locale tiếng Việt bằng lệnh locale-gen vi_VN.UTF-8 (chạy bằng quyền root). Sau đó locale.strxfrm đổi mỗi chuỗi thành một khóa so sánh theo quy tắc tiếng Việt, và bạn truyền nó vào key:

python
import locale

ten = ["Bình", "An", "Đức", "Ánh", "Dũng", "Châu", "Ân", "Anh", "Ơn", "Oanh", "Ảnh", "Ạnh"]
locale.setlocale(locale.LC_COLLATE, "vi_VN.UTF-8")
print(sorted(ten, key=locale.strxfrm))
Terminal Python 3.15.1: locale-gen vi_VN.UTF-8 chạy xong, sorted theo locale vi_VN ra An, Anh, Ảnh, Ánh, Ạnh, Ân, Bình, Châu, Dũng, Đức, Oanh, Ơn

Cách này phụ thuộc vào máy. Tên locale trên Windows khác Linux, và máy chủ thuê thường chưa cài sẵn locale tiếng Việt, đúng như lỗi mình gặp ở trên. Theo tài liệu module locale, setlocale còn đổi thiết lập cho cả chương trình, không riêng phép sắp xếp.

Cách 2: tự viết hàm khóa theo bảng chữ cái

Hàm khoa_viet dưới đây chỉ dùng standard library (các module có sẵn khi cài Python). Hàm tách mỗi ký tự thành chữ cái gốc (a, ă, â, đ…) và dấu thanh. Khi xếp, chữ cái được so trước. Hai chuỗi có chữ cái giống hệt nhau mới xét tới dấu thanh, theo thứ tự ngang, huyền, hỏi, ngã, sắc, nặng:

python
import unicodedata

CHU = "aăâbcdđeêghiklmnoôơpqrstuưvxy"
THANH = {"": 0, "\u0300": 1, "\u0309": 2, "\u0303": 3, "\u0301": 4, "\u0323": 5}  # ngang, huyền, hỏi, ngã, sắc, nặng

def khoa_viet(chuoi):
    goc, thanh = [], []
    for kt in chuoi.lower():
        tach = unicodedata.normalize("NFD", kt)
        dau = "".join(c for c in tach if c in THANH and c)
        chu = unicodedata.normalize("NFC", "".join(c for c in tach if c not in THANH))
        goc.append(CHU.index(chu) if chu in CHU else 100 + ord(chu))
        thanh.append(THANH[dau])
    return goc, thanh

lop = ["Nguyễn Văn Bình", "Trần Thị An", "Lê Minh Đức", "Phạm Thu Anh", "Đỗ Hoàng An"]
print(sorted(lop, key=lambda t: (khoa_viet(t.split()[-1]), khoa_viet(t))))

Dòng cuối xếp danh sách lớp kiểu Việt Nam: theo tên (từ cuối cùng) trước, trùng tên thì xét cả họ tên.

Terminal Python 3.15.1: tieng_viet_khoa.py xếp 12 tên ra An, Anh, Ảnh, Ánh, Ạnh, Ân, Bình, Châu, Dũng, Đức, Oanh, Ơn; danh sách lớp xếp theo tên ra Đỗ Hoàng An, Trần Thị An, Phạm Thu Anh, Nguyễn Văn Bình, Lê Minh Đức

Với 12 tên trên, hai cách cho cùng một thứ tự. Mình thử thêm 2.000 nhóm, mỗi nhóm năm chuỗi ghép ngẫu nhiên từ các chữ có dấu (không phải tên thật), thì 1.426 nhóm cho thứ tự khác nhau. Ví dụ locale vi_VN xếp "ắeu" trước "aod", còn khoa_viet xếp "aod" trước vì a đứng trước ă trong bảng chữ cái. Với tên người thật, mình chưa gặp trường hợp lệch, nhưng nếu thứ tự phải đúng tuyệt đối (danh sách thi, danh sách in), nên kiểm lại bằng mắt vài chục dòng đầu.

sort và sorted chạy nhanh hay chậm?

Mình xếp 1 triệu số nguyên ngẫu nhiên, mỗi phép đo chạy năm lần bằng timeit (module đo thời gian có sẵn), lấy lần nhanh nhất:

Biểu đồ thời gian sorted 1 triệu số nguyên trên Python 3.15.1, máy CPU AIDev Cloud: đã xếp sẵn 86,5 ms, xếp ngược sẵn 91,1 ms, ngẫu nhiên 348,8 ms, ngẫu nhiên reverse 397,1 ms, ngẫu nhiên key=lambda 573,8 ms

Phép đo (1 triệu phần tử)

Thời gian (ms)

sorted() số ngẫu nhiên

348,8

list.sort() trên bản chép của cùng list

357,3

chỉ chép list bằng copy()

10,0

sorted() list đã xếp sẵn

86,5

sorted() với key=lambda x: -x

573,8

sorted() 1 triệu chuỗi số

593,1

sort() và sorted() nhanh như nhau. Bản sort() phải chép list trước để mỗi lần đo đều xếp dữ liệu ngẫu nhiên, nhưng phép chép chỉ tốn 10 ms. List đã xếp sẵn hoặc xếp ngược sẵn được xếp nhanh gấp khoảng bốn lần, vì thuật toán của Python nhận ra các đoạn đã có thứ tự. Thêm key=lambda làm chậm 65%, vì Python phải gọi hàm lambda một triệu lần. Mình đo lại lần hai, các số lệch tới 13% nhưng thứ tự nhanh chậm giữ nguyên, riêng key=lambda lần này chậm hơn 42%.

Về bộ nhớ, sorted() trên list 1 triệu số làm RAM đỉnh của chương trình tăng từ 50,4 MB lên 61,8 MB, tức thêm 11,4 MB cho list mới. List vài trăm nghìn phần tử trở xuống thì khoản này không đáng lo.

Những lỗi nào hay gặp khi sắp xếp?

Gán lại kết quả của sort(), xếp list trộn chữ và số

text
TypeError: 'NoneType' object is not subscriptable
TypeError: '<' not supported between instances of 'str' and 'int'
Terminal Python 3.15.1: ds = ds.sort() rồi in phần tử đầu của ds báo TypeError NoneType object is not subscriptable; sorted một list trộn số và chuỗi báo TypeError less than not supported between instances of str and int

Lỗi đầu đến từ dòng ds = ds.sort(): sort() trả về None, nên ds thành None và không lấy phần tử được nữa. Sửa bằng cách chỉ viết ds.sort(), hoặc ds = sorted(ds). Lỗi thứ hai xảy ra vì Python 3 không so được chuỗi với số. Dữ liệu đọc từ file hoặc từ input() thường bị trộn như vậy. Bạn đổi hết về số trước khi xếp.

Gọi sort() trên tuple hoặc dict

text
AttributeError: 'tuple' object has no attribute 'sort'
AttributeError: 'dict' object has no attribute 'sort'
Terminal Python 3.15.1: t.sort() trên tuple báo AttributeError tuple object has no attribute sort; d.sort() trên dict báo AttributeError dict object has no attribute sort

Tuple không sửa được nên không có sort(). Dict cũng không có. Cả hai trường hợp đều dùng sorted(), kết quả là một list.

Chuỗi số xếp sai thứ tự

Lỗi này không có thông báo nào. List ["10", "9", "100", "2"] xếp ra ['10', '100', '2', '9'], vì chuỗi được so từng ký tự từ trái sang và "1" nhỏ hơn "2". Thêm key=int thì ra đúng ['2', '9', '10', '100']:

Terminal Python 3.15.1: sorted list chuỗi 10, 9, 100, 2 ra 10, 100, 2, 9; thêm key=int ra 2, 9, 10, 100

Khi nào không nên dùng sort hay sorted?

Khi chỉ cần phần tử lớn nhất hoặc nhỏ nhất, bạn không cần tới sort và sorted trong Python: max() và min() đi qua dữ liệu một lần, nhanh hơn xếp cả list rồi lấy phần tử đầu. Khi cần vài phần tử lớn nhất trong một list rất dài, module heapq có nlargest().

Với bảng dữ liệu nhiều cột, như file Excel hay CSV hàng nghìn dòng, library pandas có sort_values() xếp theo một hoặc nhiều cột mà không phải viết lambda. Bài làm sạch dữ liệu với pandas dùng pandas trên file đơn hàng của một cửa hàng mô phỏng, nếu bạn đang làm việc với bảng thì nên đọc tiếp bài đó.

Câu hỏi thường gặp

sort() có trả về list đã sắp xếp không?

Không. list.sort() sắp xếp ngay trên list và trả về None. Muốn có kết quả để gán cho biến khác, dùng sorted().

Làm sao sắp xếp giảm dần?

Thêm reverse=True, ví dụ sorted(ds, reverse=True) hoặc ds.sort(reverse=True). Trong phép đo tốc độ của bài, reverse=True chậm hơn xếp tăng dần một chút: 14% ở lần đo đầu (397,1 so với 348,8 ms), 4% ở lần đo thứ hai.

Python dùng thuật toán sắp xếp gì?

CPython dùng một biến thể của merge sort, kết hợp với insertion sort cho các đoạn ngắn. Chi tiết nằm trong file Objects/listsort.txt của mã nguồn CPython. Người mới chỉ cần nhớ hai điều đã thấy trong phép đo: dữ liệu gần như đã xếp sẵn được xếp rất nhanh, và thứ tự của các phần tử có khóa bằng nhau luôn được giữ.

sorted() dùng được với chuỗi không?

Được, nhưng kết quả là list các ký tự, ví dụ sorted("bca") ra ['a', 'b', 'c']. Muốn ghép lại thành chuỗi, dùng "".join(sorted("bca")).

Bước tiếp theo

sort, sorted và key nằm ở chương 3 của ebook Python từ số 0, trong phần list, tuple và dict, kèm bài toán phân tích điểm thi của 200 thí sinh. Nếu bạn chưa chắc về vòng lặp mà các ví dụ ở đây dùng, xem lại bài vòng lặp for trong Python. Chương trình đếm từ và ghi chi tiêu trong bài 15 chương trình Python mẫu cũng dùng sorted() với key, bạn có thể mở ra sửa thử.

Thứ tự học đầy đủ từ biến tới OOP nằm trong bài học Python từ số 0.

Code trong bài chạy trên Python 3.15.1, máy CPU Starter của AIDev Cloud (Intel Xeon E5-2680 v4, 8 vCPU, 16 GB RAM, Ubuntu 24.04.4, glibc locale vi_VN.UTF-8), ngày 26/10/2026.

Tham vấn chuyên môn

Nguyễn Việt Chung

Nhà sáng lập AIDev và VietPlatform · Thạc sĩ AI MSE

  • Tốt nghiệp thủ khoa xuất sắc Thạc sĩ AI MSE.
  • Đồng sáng lập, CTO E-University, SVOnline: số hóa 10+ đại học, 40.000+ sinh viên.
  • Xây hệ thống AI cho doanh nghiệp tại VietPlatform.
Biên soạn: AIDev Team · Rà soát · Quy trình biên tập