⭐ Vision Engineer · Chặng 1Bài 5/11Bài học~75 phút

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__".

Python cơ bản: hàm và module

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
  1. 1. Định nghĩa và gọi hàm
  2. 2. Tham số mặc định và tham số theo tên
  3. 3. Trả về nhiều giá trị
  4. 4. Phạm vi biến
  5. 5. Type hint: ghi chú kiểu dữ liệu
  6. 6. Module: tách code ra file riêng
  7. 7. Khi nào nên tách thành hàm?
  8. Tóm tắt
  9. Bài tập

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 def là 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.
  • return trả kết quả về nơi gọi và kết thúc hàm. Hàm không có return sẽ 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ố.

Bẫy: không dùng list làm giá trị mặc địnhViết 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_cu rồi gọi cong_cu.ten_ham(): rõ hàm đến từ đâu.
  • from cong_cu import ten_ham: gọi ngắn gọn ten_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.

Thư viện chuẩn có sẵn rất nhiều moduleBạn đã dùng 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.

Bài tiếp theo
Ban biên tập HọcFree

Chúng tôi viết những bài hướng dẫn dễ hiểu, thực tế để ai cũng có thể tự học lập trình và ngoại ngữ, hoàn toàn miễn phí.