Chi tiết về Đối tượng Hàm và Code Object trong Python

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)

Thẻ: python function-object code-object introspection closures

Đăng vào ngày 25 tháng 5 lúc 06:37