Giới thiệu về Đối tượng Hàm
Khi làm việc với Python, bạn cần hiểu rằng hàm cũng là một dạng đối tượng hoàn chỉnh, không chỉ đơn thuần là khối lệnh để thực thi. Mỗi hàm sở hữu những thuộc tính nội tại giúp chúng ta kiểm soát và giám sát hành vi của nó. Để khám phá các đặc điểm sẵn có của một hàm cụ thể, chúng ta có thể sử dụng hàm tích hợp dir().
Ví dụ dưới đây định nghĩa một hàm tính tổng cơ bản:
def compute_total(x: float, y: float = 0.0) -> float:
"""Tính tổng hai giá trị đầu vào"""
return x + y
# Hiển thị các thành phần có thể truy cập
print(dir(compute_total))
Kết quả sẽ trả về danh sách các phương thức đặc biệt và thuộc tính tiêu chuẩn của đối tượng hàm.
Các Thuộc Tính Tiêu Biểu Của Hàm
Bảng sau mô tả các thuộc tính thường gặp nhất khi kiểm tra một hàm số trong Python:
| Thuộc tính | Dữ liệu trả về | Mô tả |
|---|---|---|
__name__ |
str | Tên định danh của hàm |
__qualname__ |
str | Tên đầy đủ bao gồm ngữ cảnh chứa (ví dụ: Tên lớp.Tên hàm) |
__doc__ |
str hoặc None | Chuỗi tài liệu mô tả chức năng (Docstring) |
__annotations__ |
dict hoặc None | Thông tin kiểu dữ liệu cho tham số và giá trị trả về |
__module__ |
str | Tên file module nơi hàm được khai báo |
__defaults__ |
tuple hoặc None | Giá trị mặc định cho các tham số bắt buộc theo vị trí |
__kwdefaults__ |
dict hoặc None | Giá trị mặc định cho các tham số chỉ nhận qua từ khóa |
__closure__ |
tuple of cell hoặc None | Hệ thống biến đóng gói từ phạm vi bên ngoài trong hàm con |
Ví Dụ Thực Tế Với Thuộc Tính Hàm
Ví dụ 1: Truy xuất thông tin cơ bản
Đoạn mã này minh họa cách đọc trực tiếp các meta-data của hàm:
def process_data(value: int, factor: int = 10) -> int:
"""Nhân giá trị với hệ số"""
return value * factor
print("Tên hàm:", process_data.__name__)
print("Tài liệu mô tả:", process_data.__doc__)
print("Gán kiểu:", process_data.__annotations__)
print("Tham số mặc định:", process_data.__defaults__)
Ví dụ 2: Phân biệt Tên và Tên đầy đủ trong Lớp
Khi hàm nằm bên trong một cấu trúc lớp, __qualname__ sẽ hiển thị rõ ràng hơn __name__:
class MathEngine:
@staticmethod
def multiply(v1, v2):
return v1 * v2
print("Tên ngắn gọn:", MathEngine.multiply.__name__)
print("Tên đầy đủ:", MathEngine.multiply.__qualname__)
Ví dụ 3: Tham số chỉ nhận qua từ khóa
Sử dụng ký hiệu * bắt buộc truyền tham số dưới dạng key-value:
def configure_settings(*, host, port=8080):
print(f"Connecting to {host}:{port}")
# configure_settings('localhost', 9000) # ❌ Lỗi vị trí không hợp lệ
configure_settings(host='localhost', port=9000) # ✅ Hợp lệ
print("Default KW:", configure_settings.__kwdefaults__)
Ví dụ 4: Biến đóng gói (Closure Variables)
Khi hàm con tham chiếu biến từ hàm cha, các biến đó trở thành cell variables:
def factory_function(mode, threshold):
def execute_filter(data):
return data > threshold
if mode == 'strict':
return execute_filter
return None
validator = factory_function('strict', 50)
print("Các biến tự do:", validator.__closure__)
for cell in validator.__closure__:
print("Giá trị đóng gói:", cell.cell_contents)
Cấu Trúc Đối Tượng Code (__code__)
Thuộc tính __code__ cung cấp cái nhìn sâu hơn vào bytecode của hàm. Đây là đối tượng lưu trữ các thông tin biên dịch.
| Thuộc tính Code | Loại dữ liệu | Ý nghĩa |
|---|---|---|
co_filename |
str | Đường dẫn file nguồn chứa đoạn mã |
co_firstlineno |
int | Số dòng khởi tạo của hàm |
co_argcount |
int | Số lượng tham số vị trí thông thường |
co_posonlyargcount |
int | Số lượng tham số bắt buộc theo vị trí (sử dụng dấu /) |
co_kwonlyargcount |
int | Số lượng tham số bắt buộc theo từ khóa |
co_nlocals |
int | Tổng số biến cục bộ được sử dụng |
co_varnames |
tuple | Danh sách tên các biến cục bộ và tham số |
co_freevars |
tuple | Danh sách các biến tự do mà hàm con tham chiếu |
co_cellvars |
tuple | Danh sách biến trong hàm cha được dùng bởi hàm con |
co_names |
tuple | Danh sách tên biến toàn cục hoặc hàm khác được gọi |
co_consts |
tuple | Các hằng số sử dụng trong hàm (bao gồm docstring) |
co_code |
bytes | Dãy instruction bytecode đã biên dịch |
co_flags |
int | Cờ trạng thái biên dịch (như flag cho generator hay async) |
Điều Chỉnh Ví Dụ Về Code Object
Ví dụ A: Kiểm tra độ phức tạp hàm
def analyze_complexity(a, b, c):
temp_result = (a + b) * c
return temp_result
byte_code_obj = analyze_complexity.__code__
print("File nguồn:", byte_code_obj.co_filename)
print("Dòng bắt đầu:", byte_code_obj.co_firstlineno)
print("Tổng biến cục bộ:", byte_code_obj.co_nlocals)
print("Tên biến:", byte_code_obj.co_varnames)
print("Hằng số bên trong:", byte_code_obj.co_consts)
Ví dụ B: Tham số Vị trí và Từ khóa hạn chế
Python hỗ trợ phân tách rõ ràng kiểu truyền tham số:
def divide(left_operand, right_operand, /):
# Tất cả tham số trước '/' phải truyền theo vị trí
return left_operand // right_operand
def power(base, *, exponent):
# Tất cả tham số sau '*' phải truyền theo từ khóa
return base ** exponent
info_div = divide.__code__
info_pow = power.__code__
print("Divide - Tổng arg:", info_div.co_argcount)
print("Divide - Arg vị trí cố định:", info_div.co_posonlyargcount)
print("Power - Arg từ khóa cố định:", info_pow.co_kwonlyargcount)
Ví dụ C: Theo dõi việc gọi hàm khác
Bộ biên dịch Python ghi lại danh sách các hàm được gọi bên trong body:
def helper_a(val): return val * 2
def helper_b(val): return val + 5
def workflow(input_val):
# Gọi hàm khác
step_one = helper_a(input_val)
return helper_b(step_one)
print("Các hàm được import/call:", workflow.__code__.co_names)
Ví dụ D: Mối quan hệ Cell Variables và Free Variables
def create_logger(prefix_msg, debug_level):
def log_message(msg_content):
# Sử dụng prefix_msg và debug_level từ scope ngoài
full_log = f"[{prefix_msg}-{debug_level}] {msg_content}"
return full_log
return log_message
logger_func = create_logger("System", "INFO")
# View trong hàm cha (nơi tạo closure)
outer_code = create_logger.__code__
print("Biến đóng gói ở hàm cha:", outer_code.co_cellvars)
# View trong hàm con (sử dụng closure)
inner_code = logger_func.__code__
print("Biến tự do ở hàm con:", inner_code.co_freevars)