Disclaimer: Tôi không sử dụng Python, vì vậy một số điều tôi nói có thể sai. Các chuyên gia Python, cảm thấy tự do để sửa tôi.
Câu hỏi hay. Tôi nghĩ rằng quan niệm sai lầm trung tâm (nếu tôi thậm chí không thể gọi nó rằng, nó hoàn toàn hợp lý như thế nào bạn đến quá trình suy nghĩ bạn sử dụng) bạn gặp nhắc bạn đặt câu hỏi là điều này:
Khi tôi viết b[0] = a
, điều đó không có nghĩa là a
là trongb
. Điều này có nghĩa là b
bao gồm tham chiếu trỏ đến điều mà a
trỏ tới.
Biến số a
và b
bản thân họ thậm chí không phải là "những thứ", và bản thân chúng cũng chỉ là con trỏ tới các "thứ" ẩn danh khác trong bộ nhớ.
Khái niệm về tài liệu tham khảo là một bước nhảy vọt lớn từ thế giới phi lập trình, vì vậy chúng ta hãy bước qua chương trình của bạn với điều này trong tâm trí:
>>> a = [0]
Bạn tạo một danh sách điều đó xảy ra để có một cái gì đó trong nó (bỏ qua cho đến bây giờ). Điều quan trọng là nó là một danh sách. Danh sách đó được lưu trữ trong bộ nhớ. Giả sử nó được lưu trữ ở vị trí bộ nhớ 1001. Sau đó, việc gán =
tạo một biến a
rằng ngôn ngữ lập trình cho phép bạn sử dụng sau này. Tại thời điểm này, có một số đối tượng danh sách trong bộ nhớ và tham chiếu đến nó mà bạn có thể truy cập với tên a
.
>>> b = [0]
Điều này cũng tương tự đối với b
. Có một danh sách mới được lưu trữ ở vị trí bộ nhớ 1002. Ngôn ngữ lập trình tạo tham chiếu b
mà bạn có thể sử dụng để tham khảo vị trí bộ nhớ và lần lượt là đối tượng danh sách.
>>> a[0], b[0] = b, a
Điều này có hai điểm giống hệt nhau, vì vậy hãy tập trung vào một: a[0] = b
. Điều này thực sự là khá lạ mắt. Nó đầu tiên đánh giá bên phải của sự bình đẳng, thấy biến b
và tìm nạp đối tượng tương ứng trong bộ nhớ (đối tượng bộ nhớ # 1002) kể từ b
là một tham chiếu đến nó. Điều gì xảy ra ở phía bên trái là không kém phần ưa thích. a
là một biến trỏ đến một danh sách (đối tượng bộ nhớ # 1001), nhưng đối tượng bộ nhớ # 1001 chính nó có một số tham chiếu của riêng nó. Thay vì những tài liệu tham khảo có tên như a
và b
, mà bạn sử dụng, các tham chiếu đó có chỉ số như 0
. Vì vậy, bây giờ, điều này làm là a
kéo lên đối tượng bộ nhớ # 1001, là một đống tham chiếu được lập chỉ mục và tham chiếu đến chỉ mục 0 (trước đây, tham chiếu này chỉ đến số thực tế 0
, đó là điều bạn đã làm trong dòng 1) và sau đó repoints tham chiếu đó (ví dụ, tham chiếu đầu tiên và duy nhất trong đối tượng bộ nhớ # 1001) đến điều mà bên phải của phương trình đánh giá. Vì vậy, bây giờ, các điểm tham chiếu 0 của đối tượng # 1001 đến đối tượng # 1002.
>>> a
[[[...]]]
>>> b
[[[...]]]
Đây chỉ là sự khéo léo được thực hiện bởi ngôn ngữ lập trình. Khi bạn chỉ cần yêu cầu đánh giá a
, nó sẽ kéo đối tượng bộ nhớ (danh sách ở vị trí # 1001), phát hiện bằng cách sử dụng phép thuật riêng của nó là nó vô hạn và tự hiển thị như vậy.
>>> a == b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded in cmp
Sự thất bại của tuyên bố này liên quan đến cách so sánh Python. Khi bạn so sánh một đối tượng với chính nó, nó ngay lập tức đánh giá đúng. Khi bạn so sánh và đối tượng với một đối tượng khác, nó sử dụng "ma thuật" để xác định liệu sự bình đẳng phải đúng hay sai. Trong trường hợp các danh sách trong Python, nó xem xét mọi mục trong mỗi danh sách và kiểm tra xem chúng có bằng nhau hay không (lần lượt sử dụng các phương thức kiểm tra bình đẳng riêng của các mục). Vì vậy, khi bạn thử a == b
. Những gì nó làm là lần đầu tiên đào b (đối tượng # 1002) và một (đối tượng # 1001) và sau đó nhận ra rằng họ là những thứ khác nhau trong bộ nhớ để đi đến kiểm tra danh sách đệ quy của nó. Nó thực hiện điều này bằng cách lặp qua hai danh sách. Đối tượng # 1001 có một phần tử có chỉ mục 0 trỏ đến đối tượng # 1002. Đối tượng # 1002 có một phần tử có chỉ mục 0 trỏ đến đối tượng # 1001. Do đó, chương trình kết luận rằng đối tượng # 1001 và # 1002 bằng nhau nếu tất cả tham chiếu của chúng trỏ đến cùng một thứ, nếu # 1002 (tham chiếu duy nhất của # 1001 trỏ tới) và # 1001 (điểm tham chiếu duy nhất của # 1002) là giống nhau cả thôi. Kiểm tra bình đẳng này không bao giờ có thể dừng lại. Điều tương tự sẽ xảy ra trong bất kỳ danh sách nào không dừng lại. Bạn có thể làm c = [0]; d = [0]; c[0] = d; d[0] = c
và a == c
sẽ tăng cùng một lỗi.
>>> a[0] == b
True
Như tôi đã đề cập trong đoạn trước, điều này ngay lập tức giải quyết thành sự thật vì Python lấy phím tắt. Không cần so sánh nội dung danh sách vì a[0]
trỏ đến đối tượng # 1002 và b
trỏ tới đối tượng # 1002. Python phát hiện rằng chúng giống hệt nhau theo nghĩa đen (chúng giống nhau) và thậm chí không bận tâm kiểm tra nội dung.
>>> a[0][0] == b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded in cmp
Điều này trở lại là lỗi vì a[0][0]
kết thúc trỏ đến đối tượng # 1001. Việc kiểm tra danh tính không thành công và rơi trở lại vào kiểm tra nội dung đệ quy, mà không bao giờ kết thúc.
>>> a[0][0][0] == b
True
Một lần nữa, a[0][0][0]
điểm phản đối # 1002, cũng như b
. Việc kiểm tra đệ quy được bỏ qua và so sánh ngay lập tức trả về true.ngựa bất kham mức
cao jabber không liên quan trực tiếp đến đoạn mã cụ thể của bạn:
- Vì tất cả có là tài liệu tham khảo đề cập đến đối tượng khác, mặc dù có là những gì dường như là "vô hạn" làm tổ, đối tượng được đề cập bởi
a
(như tôi đã gọi đối tượng # 1001) và đối tượng được gọi là b
(# 1002) đều có cùng kích thước trong bộ nhớ. Và kích thước đó thực sự cực kỳ nhỏ vì tất cả chúng là những danh sách trỏ đến các vị trí bộ nhớ khác tương ứng.
- Cũng cần lưu ý rằng bằng ít ngôn ngữ "hào phóng", so sánh hai tham chiếu với
==
trả về true
chỉ nếu các đối tượng bộ nhớ trỏ đến giống nhau. Java là một ví dụ về điều này. Các quy ước phong cách đã nổi lên trong các ngôn ngữ như vậy là để xác định một phương pháp/chức năng trên các đối tượng mình (đối với Java, nó được gọi là equals()
) để thực hiện kiểm tra bình đẳng tùy chỉnh. Python thực hiện điều này trong hộp cho danh sách. Tôi không biết cụ thể về Python, nhưng ít nhất trong Ruby, ==
bị quá tải theo nghĩa là khi bạn thực hiện someobject == otherobject
, nó thực sự gọi một phương thức có tên là ==
trên someobject
(bạn có thể ghi đè). Về lý thuyết, sẽ không có gì ngăn cản bạn thực hiện someobject == otherobject
trả lại cái gì đó khác với boolean.
Đó thực sự là một tính năng thú vị. – phimuemue
Câu hỏi hay. Tôi thực sự thích tính năng này của Python, mặc dù tôi chưa bao giờ tìm thấy một sử dụng cho nó. Nó sẽ là tuyệt vời nếu ai đó có thể đưa ra một ứng dụng thực tế của tính năng này. Hoặc viết một mô-đun để tạo danh sách chứa tất cả các danh sách: P – andronikus
@andronikus: http://xkcd.com/468/ –