Định nghĩa hàm
Bạn sẽ học được
- Cú pháp
def, thân hàm và cách gọi. - Docstring và type hint — tài liệu ngay trong mã.
- Vì sao hàm giúp tái sử dụng và đặt tên cho logic.
- Khi nào tách một khối lệnh thành hàm.
- Lỗi thường gặp khi định nghĩa và gọi hàm.
Khái niệm cốt lõi
Hàm là một khối lệnh có tên, nhận đầu vào (tham số) và trả đầu ra (return). Định nghĩa bằng def:
def chao(name, greeting="Xin chào"):
"""Trả câu chào cho một người."""
return f"{greeting} {name}"
print(chao("An"))
print(chao("Bình", "Chào buổi sáng"))Xin chào An
Chào buổi sáng BìnhCấu trúc:
| Phần | Trong ví dụ |
|---|---|
| Từ khoá | def |
| Tên hàm | chao |
| Tham số | name, greeting="Xin chào" |
| Dấu hai chấm | : |
| Thân hàm | Các dòng thụt lề bên dưới |
| Docstring | Chuỗi ba nháy đầu thân |
Tên hàm dùng snake_case và thường bắt đầu bằng động từ: tinh_tong, doc_file, loc_don_hang. Tên rõ ràng giúp đọc code như đọc câu.
Gọi hàm
Gọi bằng tên + ngoặc tròn. Thiếu ngoặc là lỗi rất hay gặp:
def greet(name):
return f"Xin chào {name}"
print(greet) # in đối tượng hàm
print(greet("An")) # gọi hàm<function greet at 0x...>
Xin chào Angreet là đối tượng hàm; greet("An") mới là lời gọi. Khi thấy output dạng <function ...>, hãy kiểm tra dấu ngoặc.
Hàm phải được định nghĩa trước khi gọi trong luồng thực thi:
print(chao("An"))
def chao(name):
return f"Xin chào {name}"NameError: name 'chao' is not definedPython thực thi file từ trên xuống; def tạo tên tại thời điểm chạy tới dòng đó. Đặt toàn bộ định nghĩa hàm ở đầu file, phần gọi ở cuối, hoặc trong if __name__ == "__main__":.
Docstring
Chuỗi ba nháy ngay sau def là docstring — tài liệu của hàm, truy cập được bằng __doc__ hoặc help():
def chao(name, greeting="Xin chào"):
"""Trả câu chào cho một người."""
return f"{greeting} {name}"
print(chao.__doc__)Trả câu chào cho một người.Docstring không ảnh hưởng logic nhưng là tài liệu sống: hiện trong editor, trong help(), và trong tài liệu sinh tự động. Viết một dòng mô tả làm gì, không mô tả lặp lại tên hàm. Với hàm phức tạp, thêm mục tham số và giá trị trả về.
Type hint
Python không bắt buộc khai báo kiểu, nhưng có thể ghi chú:
def chao(name: str, greeting: str = "Xin chào") -> str:
return f"{greeting} {name}"Type hint không kiểm tra lúc chạy — truyền số vào name vẫn chạy đến khi f-string ép thành chuỗi. Giá trị của nó nằm ở editor (gợi ý, cảnh báo) và công cụ kiểm tra tĩnh như mypy/pyright. Với dự án nhỏ, hint ở chữ ký hàm là đủ; không cần hint mọi biến.
Khi nào tách hàm
Tách khi:
- Cùng một khối logic xuất hiện từ hai lần trở lên.
- Một đoạn code cần tên để người đọc hiểu ý định.
- Muốn kiểm thử riêng một phần mà không chạy toàn bộ chương trình.
- Thân hàm hiện tại quá dài, có thể chia thành các bước có nghĩa.
Không nên tách chỉ để code ngắn hơn. Một hàm một dòng gọi hàm khác có thể làm code khó theo dõi nếu tên không mang thêm thông tin.
Ví dụ trước và sau:
# Trước: trộn tính toán và in
rows = [("An", 85), ("Bình", 92)]
total = 0
for name, score in rows:
total += score
print(f"Trung bình: {total / len(rows):.1f}")# Sau: tách logic có tên
def diem_trung_binh(rows):
if not rows:
return 0.0
return sum(score for _, score in rows) / len(rows)
print(f"Trung bình: {diem_trung_binh(rows):.1f}")Hàm sau kiểm thử được với nhiều bộ dữ liệu, kể cả list rỗng, mà không cần đọc bàn phím hay bắt output.
Hàm lồng nhau
Có thể định nghĩa hàm trong hàm. Hàm trong chỉ dùng được trong phạm vi hàm ngoài, trừ khi được trả ra ngoài:
def tao_bo_dem():
count = 0
def dem():
nonlocal count
count += 1
return count
return dem
bo_dem = tao_bo_dem()
print(bo_dem(), bo_dem(), bo_dem())1 2 3Đây là closure — hàm trong giữ biến của hàm ngoài. Chương này chỉ cần nhận diện; phạm vi biến sẽ giải thích kỹ ở bài 4.
Thực hành
- Viết hàm
chao(name, greeting="Xin chào")và gọi với một, hai tham số. - In
help(chao)hoặcchao.__doc__để xem docstring. - Thêm type hint cho hàm và thử truyền sai kiểu để thấy Python không chặn.
- Gọi hàm trước khi định nghĩa và đọc
NameError. - Tách đoạn tính điểm trung bình thành hàm, xử lý list rỗng.
- Đổi tên hàm theo động từ + đối tượng (
tinh_diem_trung_binh) và nhận xét độ rõ.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
In ra <function ...> | Gọi thiếu ngoặc | Thêm () |
NameError khi gọi | Hàm định nghĩa sau lời gọi | Đưa def lên trước |
IndentationError | Thân hàm không thụt lề | Thụt 4 dấu cách |
SyntaxError ở def | Thiếu : hoặc sai tên | Kiểm tra chữ ký |
| Hàm không trả gì | Quên return | Thêm return |
| Type hint không báo lỗi | Hint không kiểm tra lúc chạy | Dùng mypy/pyright nếu cần |
| Docstring không hiện | Đặt không phải câu đầu thân | Đưa lên dòng đầu sau def |
Tóm tắt
def ten_ham(tham_so):+ thân thụt lề; gọi bằngten_ham(...).- Docstring là tài liệu sống; type hint hỗ trợ editor, không kiểm tra lúc chạy.
- Định nghĩa trước khi gọi; đặt phần chạy trong
if __name__ == "__main__". - Tách hàm khi có logic lặp lại, cần tên, hoặc cần kiểm thử riêng.
- Tên hàm nên là động từ mô tả việc nó làm.
Use ← → to move between lessons