Hàm là một khối các bước có tên
Bất cứ khi nào bạn thấy mình viết lại cùng vài dòng code nhiều lần, hoặc muốn đặt tên cho một phần logic nhỏ, đó là lúc bạn nên viết một hàm (function). Hàm là công cụ thực sự đầu tiên giúp kiểm soát độ phức tạp trong một chương trình Python.
Cấu trúc cơ bản:
Phân tích từng phần:
deflà từ khóa bắt đầu một định nghĩa hàm.greetlà tên hàm.(name)là danh sách tham số, tức các đầu vào mà hàm nhận.- Dấu hai chấm kết thúc phần khai báo; khối thụt lề bên dưới là phần thân.
greet("Ada")là một lời gọi hàm. Python chạy phần thân vớinameđược gán bằng"Ada".
Hàm nằm im cho đến khi được gọi. Định nghĩa hàm không làm nó chạy. Gọi greet("Ada") mới làm nó chạy.
Tham số và đối số
Tham số (parameter) là cái tên nằm trong định nghĩa hàm. Đối số (argument) là giá trị được truyền vào khi bạn gọi hàm. Trong trò chuyện thường ngày thì không quan trọng lắm, nhưng phân biệt được hai khái niệm này sẽ giúp bạn khi đọc thông báo lỗi.
Ở đây base và exponent là tham số. 2 và 10 là đối số. Python gán chúng theo thứ tự: đối số đầu tiên cho tham số đầu tiên, và cứ thế tiếp tục.
return: gửi giá trị trở lại
print in ra màn hình. return trao một giá trị lại cho nơi gọi hàm để họ dùng tiếp:
Không có return, hàm mặc định trả về None:
return cũng thoát khỏi hàm ngay lập tức. Bạn sẽ thường thấy return sớm được dùng để xử lý các trường hợp đặc biệt trước khi vào logic chính:
Return sớm giúp phần thân chính không bị lồng sâu trong if/else.
Tham số mặc định
Một tham số có thể có giá trị mặc định, được dùng khi nơi gọi không truyền vào:
Mọi tham số có giá trị mặc định phải đứng sau các tham số không có giá trị mặc định. def f(a, b=1, c): là lỗi cú pháp.
Bẫy giá trị mặc định mutable
Đây là cái bẫy nổi tiếng nhất của Python. Xem nhé:
Bạn sẽ nghĩ mỗi lần gọi đều bắt đầu với một list rỗng. Thực tế, list mặc định được dùng chung giữa các lần gọi, và các phần tử cứ thế dồn lại. Python tính giá trị mặc định một lần duy nhất, lúc hàm được định nghĩa, và dùng lại đúng list đó mãi mãi.
Cách an toàn là dùng None làm giá trị mặc định và tạo list bên trong hàm:
Giờ thì mỗi lần gọi mà không truyền items sẽ nhận một list mới. Quy tắc này áp dụng cho list, dict, set, tức mọi thứ mutable (có thể thay đổi).
Đối số keyword
Bạn có thể truyền đối số theo tên thay vì theo vị trí. Cách này cho phép bỏ qua các tham số ở giữa và khiến những lời gọi dài dễ đọc hơn:
Đối số theo vị trí và đối số keyword có thể dùng chung, nhưng đối số theo vị trí phải đứng trước:
Khi một hàm có hơn ba hay bốn tham số, ưu tiên đối số keyword sẽ khiến lời gọi hàm dễ đọc hơn nhiều.
Tham số chỉ theo vị trí và chỉ theo keyword
Python cho phép bạn đánh dấu một số tham số chỉ được truyền theo vị trí (với /) hoặc chỉ theo keyword (với *):
Bạn chưa cần đến chúng ngay từ ngày đầu. Chúng trở nên hữu ích khi bạn thiết kế API và muốn quy định chặt cách nơi gọi dùng hàm của bạn.
Docstring và cách đặt tên
Dòng đầu tiên bên trong hàm có thể là một docstring, tức một chuỗi trong ba dấu nháy mà Python coi là tài liệu:
Các công cụ như help(), phần gợi ý trong IDE và trình tạo tài liệu tự động đọc docstring. Một dòng thôi cũng tốt hơn nhiều so với không có gì.
Và hãy nghĩ kỹ về cách đặt tên:
- Tên hàm nên là động từ:
fetch_profile,compute_total,is_valid. - Dùng
lower_snake_case. - Hàm trả về boolean thường bắt đầu bằng
is_,has_hoặccan_.
Một cái tên tốt khiến chỗ gọi hàm tự giải thích được mà không cần chú thích.
Hàm thuần dễ suy luận hơn
Một hàm thuần (pure function) luôn trả về cùng một kết quả với cùng một đầu vào và không có tác dụng phụ: nó không thay đổi trạng thái toàn cục, không in ra stdout (trừ khi để gỡ lỗi) và không ghi vào tệp.
Cả hai đều có chỗ dùng, nhưng với code bạn muốn tái sử dụng và kiểm thử, hãy cố gắng hướng tới hàm thuần nhiều nhất có thể.
Một ví dụ nhỏ chạy được
Một hàm ngắn kết hợp giá trị mặc định, đối số keyword và return:
Tiếp theo: danh sách đối số linh hoạt
Đôi khi bạn không biết một hàm sẽ được gọi với bao nhiêu đối số, hoặc bạn muốn chuyển tiếp mọi đối số nhận được sang một hàm khác. *args và **kwargs giải quyết chuyện đó. Đó là trang tiếp theo.
Câu hỏi thường gặp
Làm sao để định nghĩa một hàm trong Python?
Dùng từ khóa def, theo sau là tên hàm, cặp ngoặc tròn chứa tham số và dấu hai chấm. Khối lệnh thụt lề bên dưới là phần thân hàm. Ví dụ: def greet(name): print(f'Hi, {name}').
return trong Python dùng để làm gì?
return gửi một giá trị về cho nơi đã gọi hàm. Hàm không có return rõ ràng sẽ tự động trả về None. Ngay khi return chạy, hàm kết thúc lập tức, và mọi code phía sau nó trong cùng khối lệnh bị bỏ qua.
Tham số mặc định trong Python là gì?
Tham số mặc định cho phép một tham số có sẵn giá trị, được dùng khi nơi gọi không truyền vào. def greet(name='friend'): nghĩa là gọi greet() không có đối số sẽ dùng 'friend'. Đừng bao giờ dùng giá trị mặc định mutable như []; xem bên dưới để biết cách an toàn hơn.