Set và tuple
Bạn sẽ học được
- Set loại trùng lặp, kiểm tra thành viên nhanh và hỗ trợ hợp/giao/hiệu.
- Vì sao set không giữ thứ tự và không chứa được list.
- Cách khử trùng nhưng giữ thứ tự ban đầu.
- Tuple là dãy bất biến, dùng làm khóa và trả nhiều giá trị.
- Khi nào chọn set, tuple hay list.
Khái niệm cốt lõi
Set là tập hợp không trùng lặp, không có thứ tự. Tuple là dãy có thứ tự nhưng bất biến. Cả hai đều dùng ngoặc tròn/móc, nhưng mục đích khác hẳn list.
print(sorted(set([1, 2, 2, 3])))[1, 2, 3]set tự bỏ phần tử trùng. Vì không giữ thứ tự, đừng dựa vào thứ tự khi in — hãy sorted() nếu cần kết quả ổn định.
Phép toán tập hợp
a = {1, 2, 3}
b = {3, 4}
print(sorted(a | b), sorted(a & b), sorted(a - b))[1, 2, 3, 4] [3] [1, 2]| Phép | Ký hiệu | Ý nghĩa |
|---|---|---|
| Hợp | a | b | Có trong a hoặc b |
| Giao | a & b | Có trong cả hai |
| Hiệu | a - b | Có trong a, không trong b |
| Hiệu đối xứng | a ^ b | Có trong đúng một bên |
Đây là công cụ rất tiện để so sánh danh sách: mã đơn hàng ngày hôm qua so với hôm nay, tập email đã đăng ký so với tập đã gửi.
yesterday = {"A1", "A2", "A3"}
today = {"A2", "A3", "A4"}
print(sorted(today - yesterday)) # đơn mới
print(sorted(yesterday & today)) # đơn còn
print(sorted(yesterday - today)) # đơn biến mất['A4']
['A2', 'A3']
['A1']Thêm, xoá và kiểm tra
a = {1, 2, 3}
a.add(5)
a.discard(99)
print(sorted(a))[1, 2, 3, 5]| Thao tác | Việc nó làm | Khi phần tử không có |
|---|---|---|
add(x) | Thêm | — |
discard(x) | Xoá nếu có | Không lỗi |
remove(x) | Xoá | KeyError |
pop() | Lấy phần tử bất kỳ | KeyError nếu rỗng |
discard an toàn hơn remove khi bạn không chắc phần tử tồn tại. Kiểm tra thành viên bằng in là O(1) trung bình — nhanh hơn nhiều so với list O(n):
ids = {"u1", "u2", "u3"}
print("u2" in ids, "u9" in ids)True FalseVì vậy khi phải kiểm tra x in collection nhiều lần trên dữ liệu lớn, chuyển collection sang set trước là tối ưu đơn giản và hiệu quả.
Set chỉ chứa giá trị hashable
{[1, 2]}TypeError: unhashable type: 'list'List thay đổi được nên không băm ổn định; set cần băm để tra cứu nhanh. Muốn chứa nhiều giá trị bất biến, dùng tuple: {(1, 2), (3, 4)}. Tương tự, dict không dùng list làm khóa được.
Khử trùng nhưng giữ thứ tự
set phá thứ tự, nên list(set(ids)) không bảo toàn thứ tự gốc. Khi cần giữ lần xuất hiện đầu tiên, dùng dict — từ Python 3.7 dict giữ thứ tự chèn:
ids = [3, 1, 3, 2, 1]
print(list(dict.fromkeys(ids)))[3, 1, 2]dict.fromkeys(ids) tạo dict với các khóa theo thứ tự gặp đầu tiên, giá trị None; lấy list() khóa là xong. Đây là mẹo chuẩn để "unique có thứ tự" mà không cần thư viện ngoài.
Tuple — dãy bất biến
point = (10.5, 106.7)
x, y = point
print(x, y)10.5 106.7Tuple dùng được làm khóa dict vì bất biến. Nó cũng là cách trả nhiều giá trị từ hàm:
def min_max(nums):
return min(nums), max(nums)
low, high = min_max([4, 9, 1])
print(low, high)1 9Thực ra hàm trả một tuple (1, 9); phép gán mở gói (unpacking) tách thành hai biến. Có thể dùng dấu * để gom phần còn lại: first, *rest = (1, 2, 3).
Tuple không sửa được:
point = (10.5, 106.7)
point[0] = 0TypeError: 'tuple' object does not support item assignmentNhưng nếu tuple chứa list bên trong, list đó vẫn sửa được — bất biến chỉ ở lớp ngoài.
Hoán đổi giá trị không cần biến tạm:
p, q = 1, 2
p, q = q, p
print(p, q)2 1Tuple một phần tử và namedtuple
print(type((1)), type((1,)))<class 'int'> <class 'tuple'>(1) chỉ là số 1 trong ngoặc; tuple một phần tử bắt buộc có dấu phẩy (1,). Đây là lỗi im lặng: code chạy nhưng giá trị không phải tuple.
Khi tuple có nhiều trường và bạn muốn truy cập theo tên, dùng namedtuple:
from collections import namedtuple
Point = namedtuple("Point", "x y")
p = Point(10.5, 106.7)
print(p, p.x)Point(x=10.5, y=106.7) 10.5Namedtuple vẫn là tuple — bất biến, nhẹ, unpack được — nhưng đọc hơn p[0]. Với cấu trúc phức tạp hơn, dataclass phù hợp hơn.
Chọn cấu trúc nào?
| Nhu cầu | Chọn |
|---|---|
| Dãy có thứ tự, cần sửa | list |
| Dãy cố định, làm khóa, trả nhiều giá trị | tuple |
| Loại trùng, kiểm tra thành viên nhanh | set |
| Ánh xạ khóa → giá trị | dict |
| Khử trùng giữ thứ tự | dict.fromkeys |
| Tập bất biến làm khóa | frozenset |
Thực hành
- Khử trùng một list số bằng
set, in kết quả đãsorted. - Tính hợp, giao, hiệu của hai tập mã đơn hàng.
- Dùng
discardvàremovevới phần tử không tồn tại, so sánh lỗi. - Khử trùng list ID nhưng giữ thứ tự bằng
dict.fromkeys. - Viết hàm trả
(min, max)và unpack kết quả. - Thử tạo tuple một phần tử không có dấu phẩy và kiểm tra
type.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
| Thứ tự set thay đổi | Set không giữ thứ tự | sorted() khi cần ổn định |
TypeError: unhashable | Chứa list/dict | Dùng tuple hoặc frozenset |
| Mất thứ tự sau khử trùng | list(set(...)) | list(dict.fromkeys(...)) |
KeyError khi remove | Phần tử không có | discard hoặc kiểm tra in |
(1) không phải tuple | Thiếu dấu phẩy | Viết (1,) |
| Sửa tuple báo lỗi | Tuple bất biến | Tạo tuple mới hoặc dùng list |
in trên list chậm | List tìm tuyến tính | Chuyển sang set |
Tóm tắt
- Set khử trùng, kiểm tra
inO(1), hỗ trợ| & - ^. - Set không giữ thứ tự và chỉ chứa giá trị hashable.
dict.fromkeyskhử trùng mà giữ thứ tự.- Tuple bất biến: khóa dict, trả nhiều giá trị, unpack.
- Tuple một phần tử cần dấu phẩy
(x,).
Use ← → to move between lessons