0
0
Lập trình
Admin Team
Admin Teamtechmely

Giải Mã Bí Ẩn Python: Bạn Có Thể Giải Được Mã Này Không?

Đăng vào 19 giờ trước

• 6 phút đọc

Giải Mã Bí Ẩn Python: Bạn Có Thể Giải Được Mã Này Không?

Giới thiệu

Bài viết này sẽ đưa bạn vào một thử thách thú vị liên quan đến một trong những khái niệm cơ bản nhất trong Python. Đây là một câu đố đơn giản nhưng lại chứa đựng nhiều điều bất ngờ mà ngay cả những lập trình viên dày dạn kinh nghiệm cũng có thể bị đánh lừa. Hãy cùng tìm hiểu và kiểm tra khả năng của bạn nhé!

Thử thách

Trước khi đi vào giải thích, hãy bắt đầu với một câu đố. Hãy xem đoạn mã Python sau và dự đoán đầu ra của nó:

python Copy
 a = 500
 b = a
 a = a + 100

 list1 = [1, 2]
 list2 = list1
 list1.append(3)

 print(f"b là {b}")
 print(f"list2 là {list2}")

Bạn nghĩ đầu ra sẽ là gì?

A) b là 600list2 là [1, 2]
B) b là 500list2 là [1, 2, 3]
C) b là 500list2 là [1, 2]
D) b là 600list2 là [1, 2, 3]

Hãy dành một chút thời gian để suy nghĩ về câu trả lời. Đoạn mã có vẻ đơn giản, nhưng có điều gì đó tinh tế đang diễn ra mà ngay cả những lập trình viên có kinh nghiệm cũng có thể gặp khó khăn.

Cuộn xuống khi bạn đã sẵn sàng cho câu trả lời...

Câu trả lời

Câu trả lời đúng là B: b là 500list2 là [1, 2, 3].

Nếu bạn đã đoán đúng, tuyệt vời! Nếu không, đừng lo lắng—hành vi này làm khó nhiều lập trình viên Python. Chìa khóa để hiểu điều này nằm ở một khái niệm quan trọng mà nhiều bài hướng dẫn thường bỏ qua.

Bí ẩn được tiết lộ

Những hành vi khác nhau trong mã của chúng ta không phải là ngẫu nhiên hay không nhất quán. Chúng là kết quả hợp lý của cách Python xử lý hai loại đối tượng khác nhau: có thể thay đổi (mutable)không thể thay đổi (immutable).

Hãy cùng phân tích kỹ từng bước một.

Phần 1: Điều bất ngờ với số nguyên

python Copy
 a = 500
 b = a
 a = a + 100

Đây là những gì Python thực sự đang làm:

  1. a = 500: Tạo một đối tượng số nguyên chứa 500 và gán tên a trỏ tới nó.
  2. b = a: Khiến b trỏ tới cùng một đối tượng 500a đang trỏ tới.
  3. a = a + 100: Đây là dòng quan trọng. Nó đánh giá a + 100 (là 600), tạo một đối tượng số nguyên mới chứa 600, và gán lại a trỏ tới đối tượng mới này.

Điểm quan trọng: các số nguyên là không thể thay đổi trong Python. Bạn không thể thay đổi giá trị của một đối tượng số nguyên sau khi nó được tạo ra. Khi bạn viết a = a + 100, Python không sửa đổi đối tượng 500 hiện có—nó tạo ra một đối tượng mới 600.

b chưa bao giờ được gán lại, nó vẫn trỏ tới đối tượng 500 ban đầu. Do đó: b là 500.

Phần 2: Bất ngờ với danh sách

python Copy
 list1 = [1, 2]
 list2 = list1
 list1.append(3)

Bây giờ hãy theo dõi các thao tác với danh sách:

  1. list1 = [1, 2]: Tạo một đối tượng danh sách chứa [1, 2] và gán list1 trỏ tới nó.
  2. list2 = list1: Khiến list2 trỏ tới cùng một đối tượng danh sách mà list1 đang trỏ tới.
  3. list1.append(3): Đây là sự khác biệt—điều này sửa đổi đối tượng danh sách hiện có bằng cách thêm 3 vào nó.

Điểm quan trọng: các danh sách có thể thay đổi trong Python. Khi bạn gọi append(), bạn đang sửa đổi đối tượng danh sách hiện có, không tạo ra một cái mới.

Vì cả list1list2 đều trỏ tới cùng một đối tượng danh sách, và đối tượng đó đã được sửa đổi, cả hai tên giờ đây đều tham chiếu tới [1, 2, 3]. Do đó: list2 là [1, 2, 3].

Bức tranh lớn hơn: Biến là tên, không phải hộp

Câu đố này minh họa một sự thật cơ bản về Python mà nhiều lập trình viên hiểu sai: các biến là tên trỏ tới đối tượng, không phải các hộp chứa giá trị.

Hãy nghĩ về nó như thế này:

  • Tên biến giống như một mảnh giấy dán có tên viết trên đó
  • Đối tượng giống như những chiếc hộp chứa dữ liệu thực tế
  • Gán tên dán lên một chiếc hộp
  • Nhiều tên có thể trỏ tới cùng một chiếc hộp

Khi bạn hiểu mô hình tư duy này, đoạn mã bí ẩn trở nên rõ ràng:

  • Với các đối tượng không thể thay đổi (số nguyên), các thao tác tạo ra các hộp mới và di chuyển nhãn tên tới chúng.
  • Với các đối tượng có thể thay đổi (danh sách), các thao tác sửa đổi nội dung của các hộp hiện có trong khi tất cả các nhãn tên trỏ tới hộp đó vẫn giữ nguyên.

Tại sao điều này quan trọng

Hiểu rõ tính thay đổi không chỉ mang tính học thuật—nó ngăn ngừa các lỗi thực sự. Hãy xem xét ví dụ thường gặp này trong Python:

python Copy
def add_item(item, target_list=[]):
 target_list.append(item)
 return target_list

# Điều này không hoạt động như mong đợi!
list1 = add_item("first")
list2 = add_item("second")  # Ôi! list2 giờ đây là ["first", "second"]

Danh sách mặc định [] là có thể thay đổi, vì vậy tất cả các cuộc gọi hàm đều chia sẻ cùng một đối tượng danh sách. Khi bạn hiểu tính thay đổi của đối tượng, hành vi này trở nên dễ dự đoán hơn là bí ẩn.

Kiểm tra hiểu biết của bạn

Bây giờ bạn đã biết bí mật, hãy thử dự đoán đầu ra của đoạn mã này:

python Copy
x = "hello"
y = x
x = x + " world"

dict1 = {"key": "value"}
dict2 = dict1
dict1["key"] = "modified"

print(f"y là '{y}'")
print(f"dict2 là {dict2}")

Câu trả lời: y là 'hello'dict2 là {'key': 'modified'} (chuỗi là không thể thay đổi, từ điển là có thể thay đổi).

Kết luận

Hệ thống biến trong Python rất thanh lịch khi bạn hiểu mô hình cơ bản. Các biến không chứa đối tượng—chúng trỏ tới chúng. Việc đối tượng được trỏ tới có thể sửa đổi hay không phụ thuộc vào tính thay đổi của nó.

Khái niệm này giải thích vô số hành vi của Python có vẻ bí ẩn lúc đầu. Nếu bạn nắm vững nó, bạn sẽ thấy mã Python trở nên dễ dự đoán và dễ gỡ lỗi hơn rất nhiều.

Hiểu rõ tính thay đổi chỉ là một trong nhiều khái niệm thanh lịch khiến Python trở nên mạnh mẽ. Khi bạn thấy được mẫu này, bạn sẽ bắt đầu nhận ra nó ở khắp nơi trong mã của mình.


Còn những bí ẩn Python nào khác làm bạn bối rối không? Hãy chia sẻ ví dụ của bạn trong phần bình luận—có thể có một lời giải thích đơn giản hơn bạn nghĩ! Và nếu bạn thấy điều này hữu ích, hãy theo dõi để nhận thêm thông tin về Python giúp làm sáng tỏ những điều bí ẩn trong mã.


Aaron Rose là kỹ sư phần mềm và nhà viết kỹ thuật tại tech-reader.blog và là tác giả của Think Like Genius.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào