Python cơ bản: hàm và module
Viết hàm có tham số mặc định, trả nhiều giá trị, type hint, docstring, tách module và khối if __name__ == "__main__".
Học xong bài này, bạn sẽ
- Viết hàm có tham số mặc định và giá trị trả về
- Tách code thành module và import
- Viết docstring, type hint cơ bản
Nên học trước: List, tuple, dict và set
Nội dung bài viết
Khi chương trình dài ra, bạn sẽ thấy cùng một đoạn code xuất hiện nhiều lần: kiểm tra dung sai, đặt tên file, tính độ sáng. Hàm gói đoạn code đó lại, đặt tên và dùng lại bao nhiêu lần cũng được. Module gom các hàm vào file riêng. Đây là kỹ năng phân biệt "viết được code" với "viết được phần mềm".
1. Định nghĩa và gọi hàm
def kiem_tra_dung_sai(gia_tri, danh_nghia, dung_sai):
"""Trả về True nếu gia_tri nằm trong danh_nghia ± dung_sai."""
return danh_nghia - dung_sai <= gia_tri <= danh_nghia + dung_sai
print(kiem_tra_dung_sai(12.48, 12.50, 0.10)) # True
print(kiem_tra_dung_sai(12.63, 12.50, 0.10)) # False
def+ tên hàm + danh sách tham số trong ngoặc + dấu:.- Chuỗi ba dấu nháy ngay dưới dòng
deflà docstring: mô tả hàm làm gì. VS Code hiện docstring khi bạn rê chuột lên tên hàm. returntrả kết quả về nơi gọi và kết thúc hàm. Hàm không córeturnsẽ trả vềNone.
2. Tham số mặc định và tham số theo tên
def dat_ten_anh(so_thu_tu, ket_qua, tram="ST1", duoi=".png"):
return f"{tram}_{so_thu_tu:06d}_{ket_qua}{duoi}"
print(dat_ten_anh(7, "OK")) # dùng giá trị mặc định
print(dat_ten_anh(8, "NG", tram="ST3")) # đổi một tham số theo tên
print(dat_ten_anh(ket_qua="OK", so_thu_tu=9, duoi=".jpg"))
ST1_000007_OK.png
ST3_000008_NG.png
ST1_000009_OK.jpg
Tham số có giá trị mặc định phải đứng sau tham số bắt buộc. Gọi theo tên (tram="ST3") giúp code dễ đọc khi hàm có nhiều tham số.
def them(x, ds=[]) thì list ds được tạo một lần và dùng chung giữa các lần gọi, dữ liệu sẽ dồn lại một cách khó hiểu. Dùng ds=None rồi trong hàm viết if ds is None: ds = [].3. Trả về nhiều giá trị
def thong_ke(ds):
"""Trả về (nhỏ nhất, lớn nhất, trung bình) của một list số."""
return min(ds), max(ds), sum(ds) / len(ds)
nho, lon, tb = thong_ke([12.48, 12.51, 12.47, 12.52])
print(f"min={nho} max={lon} tb={tb:.3f}")
min=12.47 max=12.52 tb=12.495
Thực chất hàm trả về một tuple, và dòng gọi "mở gói" tuple đó thành 3 biến.
4. Phạm vi biến
nguong = 128 # biến toàn cục
def phan_loai(do_sang):
ket_qua = "sáng" if do_sang > nguong else "tối" # đọc được biến toàn cục
return ket_qua
print(phan_loai(200))
# print(ket_qua) → NameError: biến tạo trong hàm chỉ tồn tại trong hàm
Biến tạo bên trong hàm là biến cục bộ, biến mất khi hàm kết thúc. Hàm đọc được biến toàn cục, nhưng cách tốt hơn là truyền mọi thứ hàm cần qua tham số: def phan_loai(do_sang, nguong=128). Hàm như vậy dễ kiểm thử và dễ dùng lại.
5. Type hint: ghi chú kiểu dữ liệu
def ty_le_ng(so_ng: int, tong: int) -> float:
"""Tỉ lệ NG trong khoảng 0–1. Trả 0.0 nếu chưa có sản phẩm nào."""
if tong == 0:
return 0.0
return so_ng / tong
print(f"{ty_le_ng(7, 1284):.2%}") # 0.55%
so_ng: int và -> float là type hint. Python không bắt buộc kiểu khi chạy, nhưng VS Code (Pylance) dùng chúng để gợi ý và cảnh báo khi bạn truyền sai. Trong dự án thật, type hint giúp đồng nghiệp hiểu hàm ngay mà không phải đọc code bên trong.
6. Module: tách code ra file riêng
Khi có nhiều hàm, hãy gom chúng vào một file và import từ chỗ khác. Tạo thư mục dự án như sau:
tram_kiem_tra/
├── cong_cu.py ← các hàm dùng chung
└── main.py ← chương trình chính
File cong_cu.py:
"""Các hàm tiện ích cho trạm kiểm tra."""
def kiem_tra_dung_sai(gia_tri: float, danh_nghia: float, dung_sai: float) -> bool:
return danh_nghia - dung_sai <= gia_tri <= danh_nghia + dung_sai
def dat_ten_anh(so_thu_tu: int, ket_qua: str, tram: str = "ST1") -> str:
return f"{tram}_{so_thu_tu:06d}_{ket_qua}.png"
if __name__ == "__main__":
# Chỉ chạy khi chạy trực tiếp: python cong_cu.py (dùng để tự kiểm tra nhanh)
assert kiem_tra_dung_sai(12.5, 12.5, 0.1)
assert dat_ten_anh(1, "OK") == "ST1_000001_OK.png"
print("cong_cu.py: các kiểm tra đều đạt")
File main.py (cùng thư mục):
# (minh họa) cần file cong_cu.py ở cùng thư mục
import cong_cu
from cong_cu import dat_ten_anh
ket_qua = "OK" if cong_cu.kiem_tra_dung_sai(12.48, 12.50, 0.10) else "NG"
print(dat_ten_anh(42, ket_qua))
Hai cách import:
import cong_curồi gọicong_cu.ten_ham(): rõ hàm đến từ đâu.from cong_cu import ten_ham: gọi ngắn gọnten_ham().
Khối if __name__ == "__main__": chỉ chạy khi bạn chạy trực tiếp file đó, không chạy khi file được import. Dùng nó để đặt đoạn tự kiểm tra hoặc chương trình chính.
math và collections. Sẽ gặp thường xuyên: pathlib (đường dẫn), json (cấu hình), csv, datetime (thời gian), time (đo tốc độ), logging (ghi log). Tất cả đều có sẵn, không cần cài.7. Khi nào nên tách thành hàm?
- Đoạn code lặp lại từ 2 lần trở lên.
- Đoạn code làm một việc có thể đặt tên rõ ràng: "tính độ sáng", "lưu kết quả".
- Hàm dài quá khoảng 30–40 dòng: tách tiếp thành hàm nhỏ hơn.
Một hàm tốt làm một việc, có tên là động từ (tinh_, kiem_tra_, luu_), nhận dữ liệu qua tham số và trả kết quả qua return thay vì in ra màn hình. Hàm trả kết quả thì dùng lại được ở mọi nơi; hàm chỉ in thì không.
Tóm tắt
def ten(tham_so): ... return ket_qua, kèm docstring.- Tham số mặc định đứng sau; gọi theo tên cho dễ đọc; không dùng list làm mặc định.
- Trả nhiều giá trị bằng tuple; biến trong hàm là cục bộ.
- Type hint giúp VS Code gợi ý và đồng nghiệp dễ hiểu.
- Module = file .py;
import;if __name__ == "__main__":.
Bài tập
Bài 1. Hàm đánh giá độ sáng
Viết hàm danh_gia_do_sang(gia_tri, toi=50, sang=200) trả về "tối", "sáng" hoặc "tốt". Gọi thử với 30, 120, 230 và với ngưỡng tùy chỉnh toi=80.
Xem đáp án
def danh_gia_do_sang(gia_tri: float, toi: float = 50, sang: float = 200) -> str:
"""Phân loại độ sáng trung bình của ảnh (0–255)."""
if gia_tri < toi:
return "tối"
if gia_tri > sang:
return "sáng"
return "tốt"
for v in (30, 120, 230):
print(v, danh_gia_do_sang(v))
print(60, danh_gia_do_sang(60, toi=80))
30 tối
120 tốt
230 sáng
60 tối
Để ý: khi đã return thì hàm kết thúc, nên không cần elif/else sau mỗi return.
Bài 2. Hàm báo cáo ca
Viết hàm bao_cao(ket_qua) nhận list các chuỗi "OK"/"NG", trả về dict {"tong": ..., "ok": ..., "ng": ..., "ty_le_ng": ...}. Xử lý trường hợp list rỗng.
Xem đáp án
def bao_cao(ket_qua: list[str]) -> dict:
tong = len(ket_qua)
ng = ket_qua.count("NG")
return {"tong": tong, "ok": tong - ng, "ng": ng, "ty_le_ng": ng / tong if tong else 0.0}
print(bao_cao(["OK", "NG", "OK", "OK"]))
print(bao_cao([]))
{'tong': 4, 'ok': 3, 'ng': 1, 'ty_le_ng': 0.25}
{'tong': 0, 'ok': 0, 'ng': 0, 'ty_le_ng': 0.0}
Bài 3. Tách module
Tạo thư mục bai_tap_5 gồm do_luong.py (chứa hàm kiem_tra_dung_sai và thong_ke) và main.py import hai hàm đó, đọc list giá trị đo, in thống kê và số NG. Thêm khối if __name__ == "__main__": trong do_luong.py để tự kiểm tra bằng assert.
Gợi ý
Dùng lại code ở mục 3 và mục 6. Chạy python do_luong.py phải in thông báo tự kiểm tra; chạy python main.py chỉ in kết quả thống kê, không in thông báo tự kiểm tra.


