Chào bạn mừng các bạn đã đến với blog của mình! Đối với lập trình Python, nếu bạn từng gặp trường hợp thay đổi một danh sách bên trong hàm và bất ngờ thấy danh sách gốc bên ngoài cũng... "biến hình" theo, thì chúc mừng, bạn đã chạm mặt một trong những khái niệm quan trọng nhất Python: Mutable (Khả biến) và Immutable (Bất biến).
Hãy cùng mình giải mã cơ chế này để không bao giờ bị Python "lừa" nữa nhé!
1. Định nghĩa đơn giản
Trong Python, mọi thứ đều là đối tượng (object). Khi bạn tạo một biến, Python sẽ tạo ra một đối tượng trong bộ nhớ và cho biến đó trỏ đến địa chỉ của đối tượng đó.
Immutable (Bất biến): Là những đối tượng không thể thay đổi giá trị sau khi đã tạo ra. Nếu bạn cố tình thay đổi, Python sẽ tạo ra một đối tượng mới hoàn toàn ở một địa chỉ mới.
Ví dụ:
int,float,bool,string,tuple.
Mutable (Khả biến): Là những đối tượng có thể thay đổi nội dung ngay tại địa chỉ bộ nhớ ban đầu của nó.
Ví dụ:
list,dict,set.
2. Sự khác biệt khi truyền vào Hàm (Function)
Đây là nơi "ma thuật" (hoặc thảm họa) xảy ra. Python sử dụng cơ chế gọi là "Pass-by-object-reference" (Truyền tham chiếu đối tượng).
Đối với Immutable (Số, Chuỗi, Tuple)
Khi bạn truyền một biến Immutable vào hàm, hàm đó nhận được một "bản sao" của tham chiếu. Nhưng vì đối tượng là bất biến, nếu bạn thay đổi nó bên trong hàm, Python sẽ tạo ra một biến cục bộ mới. Biến gốc bên ngoài sẽ không bị ảnh hưởng.
def change_number(n):
n = n + 10
print(f"Trong hàm: {n}")
num = 5
change_number(num)
print(f"Ngoài hàm: {num}")
# Kết quả:
# Trong hàm: 15
# Ngoài hàm: 5 (Vẫn giữ nguyên!)
Đối với Mutable (List, Dict)
Khi bạn truyền một Mutable object vào hàm, hàm đó cũng nhận tham chiếu đến đúng địa chỉ bộ nhớ đó. Vì đối tượng này cho phép thay đổi nội dung, mọi chỉnh sửa bên trong hàm sẽ tác động trực tiếp lên đối tượng gốc.
def add_item(my_list):
my_list.append("Surprise!")
print(f"Trong hàm: {my_list}")
list_goc = [1, 2, 3]
add_item(list_goc)
print(f"Ngoài hàm: {list_goc}")
# Kết quả:
# Trong hàm: [1, 2, 3, 'Surprise!']
# Ngoài hàm: [1, 2, 3, 'Surprise!'] (Bị thay đổi luôn!)
3. Cạm bẫy "Default Argument" (Tham số mặc định)
Một lỗi kinh điển mà ngay cả lập trình viên lâu năm cũng mắc phải là dùng Mutable object làm tham số mặc định.
to.append(element)
return to
print(add_to(1)) # [1]
print(add_to(2)) # [1, 2] -> Đáng lẽ phải là [2] chứ?
Giải thích: Danh sách to=[] chỉ được tạo ra duy nhất một lần khi định nghĩa hàm. Các lần gọi sau sẽ dùng chung cái "kho" đó.
Cách sửa: Luôn dùng to=None và kiểm tra if to is None: to = [] bên trong hàm.
Tổng kết: Quy tắc "Vàng"
| Loại | Khi thay đổi trong hàm | Tác động bên ngoài |
| Immutable | Tạo vùng nhớ mới | Không thay đổi |
| Mutable | Sửa trên vùng nhớ cũ | Bị thay đổi theo |
No comments:
Post a Comment