DHDataHọc
Lý thuyếtBài 16/304 phút đọc+10 XP

Dict

Bạn sẽ học được

  • Dict lưu cặp khóa–giá trị và tra cứu theo khóa.
  • [] khác get() ở chỗ nào.
  • Duyệt keys, values, items và thứ tự chèn.
  • Mẫu đếm và nhóm dữ liệu bằng get / setdefault.
  • Vì sao khóa phải bất biến và cách tránh KeyError.

Khái niệm cốt lõi

Dict là bảng ánh xạ từ khóa sang giá trị. Thay vì tìm theo vị trí như list, bạn tìm theo tên:

code
user = {"name": "An", "age": 28}
print(user["name"])
print(user.get("email"))
print(user.get("email", "chưa có"))
code
An
None
chưa có

Ba cách đọc khác nhau rất quan trọng:

Cách đọcKhi khóa cóKhi khóa không có
user["email"]Trả giá trịNém KeyError
user.get("email")Trả giá trịTrả None
user.get("email", "chưa có")Trả giá trịTrả giá trị mặc định

Dùng [] khi khóa bắt buộc phải có — lỗi sớm giúp phát hiện dữ liệu hỏng. Dùng get() khi khóa có thể thiếu và bạn có cách xử lý. Đừng bọc mọi truy cập trong try/except KeyError; get rõ ý hơn.

Tra cứu theo khóa trung bình là O(1) nhờ bảng băm, nhanh hơn nhiều so với tìm trong list O(n).

Thêm, sửa, xoá

code
user = {"name": "An", "age": 28}
user["age"] = 29
user["city"] = "Hà Nội"
print(user)
code
{'name': 'An', 'age': 29, 'city': 'Hà Nội'}

Gán vào khóa đã có là sửa; khóa mới là thêm. Dict giữ thứ tự chèn từ Python 3.7: sửa age không đổi vị trí của nó.

code
user = {"name": "An", "age": 28}
print(list(user))
print("age" in user, "email" in user)
print(len(user))
code
['name', 'age']
True False
2

in kiểm tra khóa, không kiểm tra giá trị. Muốn kiểm tra giá trị, dùng "An" in user.values() — O(n).

Xoá bằng del hoặc pop:

code
user = {"name": "An", "age": 28, "city": "Hà Nội"}
age = user.pop("age")
print(age, user)
del user["city"]
print(user)
code
28 {'name': 'An', 'city': 'Hà Nội'}
{'name': 'An'}

pop trả giá trị bị xoá và nhận mặc định: user.pop("phone", None) không ném lỗi. del ném KeyError nếu khóa không tồn tại.

Duyệt dict

code
user = {"name": "An", "age": 29, "city": "Hà Nội"}
for key, value in user.items():
    print(key, "=", value)
code
name = An
age = 29
city = Hà Nội
Cách duyệtNhận được
for k in userKhóa
for v in user.values()Giá trị
for k, v in user.items()Cả hai

items() là cách chuẩn khi cần cả khóa và giá trị. Không nên for k in user: print(k, user[k]) — chậm hơn và dài hơn.

Giống list, không sửa dict đang duyệt. Thêm/xoá khóa trong lúc lặp ném RuntimeError: dictionary changed size during iteration. Tạo danh sách khóa trước (for k in list(user):) hoặc xây dict mới.

Mẫu đếm

Đây là ứng dụng phổ biến nhất của dict trong phân tích dữ liệu:

code
votes = ["A", "B", "A", "C", "A"]
counts = {}
for v in votes:
    counts[v] = counts.get(v, 0) + 1
print(counts)
code
{'A': 3, 'B': 1, 'C': 1}

counts.get(v, 0) trả 0 khi chưa có khóa, nên lần đầu gặp v sẽ gán 1. Đây là lý do get có mặc định quan trọng: nếu viết counts[v] += 1, lần đầu sẽ ném KeyError.

Trong thư viện chuẩn có collections.Counter làm đúng việc này:

code
from collections import Counter
print(Counter(votes))
code
Counter({'A': 3, 'B': 1, 'C': 1})

Khi học, viết tay bằng get để hiểu cơ chế; trong dự án thật, Counter ngắn và tối ưu hơn.

Mẫu nhóm

setdefault(key, default) trả giá trị hiện có; nếu khóa chưa có thì gán default rồi trả nó. Kết hợp với append cho mẫu nhóm:

code
rows = [("An", "SQL"), ("Bình", "Python"), ("An", "Python")]
by_name = {}
for name, skill in rows:
    by_name.setdefault(name, []).append(skill)
print(by_name)
code
{'An': ['SQL', 'Python'], 'Bình': ['Python']}

Cùng kết quả có thể viết bằng defaultdict(list):

code
from collections import defaultdict

by_name = defaultdict(list)
for name, skill in rows:
    by_name[name].append(skill)

Khác biệt: defaultdict tự tạo list khi truy cập khóa thiếu, kể cả khi chỉ đọc. Điều này tiện nhưng có thể âm thầm thêm khóa rỗng — cẩn thận khi kiểm tra len.

Khóa phải bất biến

Khóa dict phải hashable: số, chuỗi, tuple chỉ chứa giá trị bất biến. List thì không:

code
{["a"]: 1}
code
TypeError: unhashable type: 'list'

Muốn dùng cặp giá trị làm khóa, dùng tuple: {(10.5, 106.7): "Hà Nội"}. Nếu cần khóa phức tạp hơn, chuyển sang chuỗi hoặc frozenset.

Thực hành

  1. Tạo dict thông tin một người, đọc bằng [], get() và get() có mặc định.
  2. Thêm khóa mới, sửa khóa cũ, kiểm tra thứ tự bằng list(d).
  3. Duyệt bằng items() và in key = value.
  4. Đếm số lần xuất hiện trong một list bằng get(v, 0) + 1.
  5. Nhóm (tên, kỹ năng) bằng setdefault.
  6. Thử dùng list làm khóa và đọc TypeError.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
KeyError: 'email'Khóa không tồn tạiDùng get hoặc kiểm tra in
TypeError: unhashable typeDùng list làm khóaĐổi sang tuple/chuỗi
RuntimeError: dictionary changed sizeThêm/xoá khi đang duyệtDuyệt trên list(d) hoặc tạo dict mới
in không tìm thấy giá trịin kiểm tra khóaDùng in d.values()
Đếm ra KeyErrorDùng d[k] += 1 lần đầud[k] = d.get(k, 0) + 1
Dict không giữ thứ tựPython cũ (< 3.7)Nâng phiên bản; hiện đại đã giữ
pop lỗi khóa thiếuKhông truyền mặc địnhd.pop(k, None)

Tóm tắt

  • Dict ánh xạ khóa → giá trị; tra cứu O(1) theo khóa.
  • [] ném KeyError; get trả None hoặc mặc định.
  • items() để duyệt cả khóa và giá trị; thứ tự chèn được giữ.
  • get(v, 0) + 1 để đếm, setdefault để nhóm.
  • Khóa phải hashable; không sửa dict khi đang duyệt.

Dùng ← → để chuyển bài