Câu trả lời:
Đó là một câu hỏi xuất sắc.
Cái nhìn sâu sắc quan trọng là các bộ dữ liệu không có cách nào để biết liệu các đối tượng bên trong chúng có thể thay đổi được hay không. Điều duy nhất làm cho một đối tượng có thể thay đổi là có một phương thức làm thay đổi dữ liệu của nó. Nói chung, không có cách nào để phát hiện điều này.
Một cái nhìn sâu sắc khác là các thùng chứa của Python không thực sự chứa bất cứ thứ gì. Thay vào đó, họ giữ các tài liệu tham khảo đến các đối tượng khác. Tương tự, các biến của Python không giống như các biến trong các ngôn ngữ được biên dịch; thay vào đó, tên biến chỉ là các khóa trong từ điển không gian tên nơi chúng được liên kết với một đối tượng tương ứng. Ned Batchhelder giải thích điều này độc đáo trong bài đăng trên blog của mình . Dù bằng cách nào, các đối tượng chỉ biết số tham chiếu của họ; họ không biết những tham chiếu đó là gì (biến, thùng chứa hoặc phần bên trong Python).
Cùng với nhau, hai cái nhìn sâu sắc này giải thích bí ẩn của bạn (tại sao một tuple bất biến "chứa" một danh sách dường như thay đổi khi danh sách cơ bản thay đổi). Trong thực tế, bộ dữ liệu không thay đổi (nó vẫn có cùng các tham chiếu đến các đối tượng khác mà nó đã làm trước đó). Bộ dữ liệu không thể thay đổi (vì nó không có phương thức đột biến). Khi danh sách thay đổi, bộ dữ liệu không được thông báo về sự thay đổi (danh sách không biết liệu nó được tham chiếu bởi một biến, bộ dữ liệu hay danh sách khác).
Trong khi chúng ta đang ở trong chủ đề này, đây là một vài suy nghĩ khác để giúp hoàn thành mô hình tinh thần của bạn về các bộ dữ liệu là gì, cách chúng hoạt động và mục đích sử dụng của chúng:
Tuples được đặc trưng ít hơn bởi tính bất biến của chúng và nhiều hơn bởi mục đích dự định của chúng.
Tuples là cách Python thu thập những mẩu thông tin không đồng nhất dưới một mái nhà. Ví dụ,
s = ('www.python.org', 80)
tập hợp một chuỗi và một số để cặp máy chủ / cổng có thể được truyền xung quanh như một ổ cắm, một đối tượng tổng hợp. Nhìn trong ánh sáng đó, nó hoàn toàn hợp lý để có các thành phần đột biến.
Bất biến đi đôi với một tài sản khác, khả năng băm . Nhưng khả năng băm không phải là một tài sản tuyệt đối. Nếu một trong các thành phần của bộ dữ liệu không thể băm được, thì bộ tổng thể cũng không thể băm được. Ví dụ, t = ('red', [10, 20, 30])
không thể băm.
Ví dụ cuối cùng cho thấy một tuple chứa một chuỗi và một danh sách. Bản thân bộ tuple không thể thay đổi (nghĩa là nó không có bất kỳ phương thức nào để thay đổi nội dung của nó). Tương tự, chuỗi là bất biến vì các chuỗi không có bất kỳ phương thức đột biến nào. Đối tượng danh sách không có phương thức đột biến, vì vậy nó có thể được thay đổi. Điều này cho thấy tính đột biến là một thuộc tính của một loại đối tượng - một số đối tượng có các phương thức đột biến và một số thì không. Điều này không thay đổi chỉ vì các đối tượng được lồng vào nhau.
Hãy nhớ hai điều. Đầu tiên, sự bất biến không phải là phép thuật - nó chỉ đơn thuần là sự vắng mặt của các phương pháp đột biến. Thứ hai, các đối tượng không biết biến hoặc vùng chứa nào đề cập đến chúng - chúng chỉ biết số tham chiếu.
Hy vọng, điều này hữu ích cho bạn :-)
hash()
bởi vì mọi thứ thừa hưởng từ đối tượng () đều có thể băm và vì vậy các lớp con cần tắt băm rõ ràng. 2) Hashability không đảm bảo tính bất biến - thật dễ dàng để tạo ra các ví dụ về các đối tượng có thể băm có thể thay đổi. 3) Tuples, giống như hầu hết các container trong Python, chỉ cần tham chiếu đến đối tượng cơ bản - chúng không có nghĩa vụ kiểm tra chúng và suy luận về chúng.
Đó là vì các bộ dữ liệu không chứa danh sách, chuỗi hoặc số. Chúng chứa các tham chiếu đến các đối tượng khác . 1 Không thể thay đổi trình tự các tham chiếu mà một tuple chứa không có nghĩa là bạn không thể thay đổi các đối tượng được liên kết với các tham chiếu đó. 2
1. Đối tượng, giá trị và loại (xem: đoạn thứ hai đến đoạn cuối)
2. Hệ thống phân cấp loại tiêu chuẩn (xem: "Trình tự bất biến")
Trước hết, từ "bất biến" có thể mang nhiều ý nghĩa khác nhau đối với những người khác nhau. Tôi đặc biệt thích cách Eric Lippert phân loại tính bất biến trong bài đăng trên blog của mình . Ở đó, ông liệt kê những loại bất biến:
Chúng có thể được kết hợp theo nhiều cách khác nhau để tạo ra nhiều loại bất biến hơn, và tôi chắc chắn tồn tại nhiều hơn. Loại bất biến mà bạn có vẻ quan tâm đến tính bất biến sâu sắc (còn được gọi là bất biến), trong đó các vật thể bất biến chỉ có thể chứa các vật thể bất biến khác.
Điểm mấu chốt của điều này là tính bất biến sâu sắc chỉ là một trong số rất nhiều loại bất biến. Bạn có thể chấp nhận bất cứ loại nào bạn thích, miễn là bạn biết rằng khái niệm "bất biến" của bạn có thể khác với khái niệm "bất biến" của người khác.
Theo tôi hiểu, câu hỏi này cần được nhắc lại như một câu hỏi về các quyết định thiết kế: Tại sao các nhà thiết kế của Python chọn tạo ra một loại trình tự bất biến có thể chứa các đối tượng có thể thay đổi?
Để trả lời câu hỏi này, chúng ta phải suy nghĩ về mục đích các bộ phục vụ: họ đóng vai trò là nhanh , có mục đích chung trình tự. Với ý nghĩ đó, nó trở nên khá rõ ràng tại sao các bộ dữ liệu là bất biến nhưng có thể chứa các đối tượng có thể thay đổi. Để dí dỏm:
Tuples nhanh và hiệu quả bộ nhớ: Tuples tạo nhanh hơn danh sách vì chúng không thay đổi. Tính không thay đổi có nghĩa là các bộ dữ liệu có thể được tạo như các hằng số và được tải như vậy, sử dụng gấp không đổi . Điều đó cũng có nghĩa là chúng tạo ra bộ nhớ nhanh hơn và hiệu quả hơn vì không cần tổng thể, v.v. Chúng chậm hơn một chút so với danh sách để truy cập mục ngẫu nhiên, nhưng lại nhanh hơn để giải nén (ít nhất là trên máy của tôi). Nếu các bộ dữ liệu có thể thay đổi, thì chúng sẽ không nhanh cho các mục đích như thế này.
Tuples là mục đích chung : Tuples cần có khả năng chứa bất kỳ loại đối tượng nào. Chúng được sử dụng để (nhanh chóng) thực hiện những việc như danh sách đối số có độ dài thay đổi (thông qua *
toán tử trong định nghĩa hàm). Nếu các tuple không thể giữ các vật thể đột biến, chúng sẽ vô dụng đối với những thứ như thế này. Python sẽ phải sử dụng các danh sách, điều này có thể sẽ làm mọi thứ chậm lại và chắc chắn sẽ ít hiệu quả hơn về bộ nhớ.
Vì vậy, bạn thấy, để thực hiện mục đích của họ, bộ dữ liệu phải là bất biến, nhưng cũng phải có khả năng chứa các đối tượng có thể thay đổi. Nếu các nhà thiết kế của Python muốn tạo ra một đối tượng bất biến đảm bảo rằng tất cả các đối tượng mà nó "chứa" cũng không thay đổi, họ sẽ phải tạo ra một loại thứ ba. Việc đạt được không có giá trị phức tạp thêm.
Bạn không thể thay đổi các id
mục của nó. Vì vậy, nó sẽ luôn chứa các mặt hàng tương tự.
$ python
>>> t = (1, [2, 3])
>>> id(t[1])
12371368
>>> t[1].append(4)
>>> id(t[1])
12371368
Tôi sẽ đi ra ở cánh một ở đây và nói rằng phần liên quan ở đây là trong khi bạn có thể thay đổi nội dung của một danh sách, hoặc trạng thái của một đối tượng, chứa trong một tuple, những gì bạn không thể thay đổi là rằng đối tượng hoặc danh sách là có. Nếu bạn có một cái gì đó phụ thuộc vào thứ [3] là một danh sách, ngay cả khi trống, thì tôi có thể thấy điều này hữu ích.
Một lý do là không có cách chung nào trong Python để chuyển đổi một loại có thể thay đổi thành một loại bất biến (xem PEP 351 bị từ chối và thảo luận được liên kết để biết lý do tại sao nó bị từ chối). Do đó, không thể đặt nhiều loại đối tượng khác nhau trong bộ dữ liệu nếu nó có hạn chế này, kể cả về bất kỳ đối tượng không thể băm nào do người dùng tạo.
Lý do duy nhất mà từ điển và bộ có hạn chế này là chúng yêu cầu các đối tượng phải được băm, vì chúng được triển khai bên trong dưới dạng bảng băm. Nhưng lưu ý rằng, trớ trêu thay, từ điển và bộ tự nó không phải là bất biến (hoặc có thể băm). Các bộ dữ liệu không sử dụng hàm băm của một đối tượng, vì vậy khả năng biến đổi của nó không thành vấn đề.
Một tuple là bất biến theo nghĩa là bản thân bộ tuple không thể mở rộng hoặc co lại, không phải tất cả các vật phẩm chứa trong đó là bất biến. Nếu không thì tuples là ngu si đần độn.