2010-09-03 34 views
7

Khi tôi thực hiện (Tôi đang sử dụng vỏ tương tác) các báo cáo này tôi có được điều này là gì:sự khác biệt giữa LIST.append (1) và DANH = LIST + [1] (Python)

L=[1,2,3] 
K=L 

L.append(4) 

L 
[1,2,3,4] 
K 
[1,2,3,4] 

nhưng khi tôi thực hiện chính xác những điều tương tự thay thế L.append (4) với L = L + [4] tôi nhận được:

L 
[1,2,3,4] 
K 
[1,2,3] 

đây có phải là một số loại điều tham khảo? Lý do tại sao điều này xảy ra?

Một điều thú vị khác mà tôi nhận thấy là L + = [4] hoạt động như .append, kỳ lạ như tôi nghĩ nó sẽ hoạt động như L = L + [4].

Làm rõ tất cả điều này sẽ được đánh giá cao.

Cảm ơn

+0

'+ =' là lạ trong python. ví dụ 'a = (1, 2); a + = (2,) 'sản lượng' (1, 2, 3) '! Điều này trái ngược hoàn toàn với tình huống danh sách trong đó nó sửa đổi danh sách tại chỗ. Không có cách nào để sửa đổi một tuple tại chỗ.đây là lý do tại sao nhiều người thích luôn sử dụng biểu mẫu 'a = a + b'. – aaronasterling

+0

không có, sau a = (1, 2); a + = (2,) a là (1,2,2) điều gì lạ về điều đó? – Kugel

+0

Tôi nghĩ anh ấy có nghĩa là 'a + = (3,)'. Và nó là loại kỳ lạ mà nó cho phép bạn sửa đổi một (không thay đổi) tuple tại chỗ như thế này. – efritz

Trả lời

2

Với append bạn đang sửa đổi danh sách trực tiếp. Với L=L+[4], bạn đang tạo bản sao của L ban đầu và thêm thành phần mới, sau đó gán kết quả đó lại cho L và vi phạm tương đương thành K.

Tôi không chắc về hành vi của +=.

+0

'+ =' dường như hoạt động giống như 'append' (tôi vừa thử nghiệm) –

+1

@Andre, nope. Nó hoạt động giống như 'mở rộng'. – habnabit

+0

bạn đúng '+ = 5' không hoạt động .. –

15
L.append(4) 

Điều này thêm phần tử vào cuối danh sách hiện tại L.

L += [4] 

Toán tử += gọi phương thức ma thuật __iadd__(). Nó chỉ ra list ghi đè phương pháp __iadd__() và làm cho nó tương đương với extend(), giống như append(), thêm các phần tử trực tiếp vào danh sách hiện có.

L = L + [4] 

L + [4] tạo ra một danh sách mới tương đương với L với 4 thêm vào cuối. Đây là danh sách mới sau đó được gán lại cho L. Bởi vì bạn đã tạo một đối tượng danh sách mới, nên K không thay đổi bởi nhiệm vụ này.

Chúng ta có thể sử dụng id() để xác định khi một tham chiếu đối tượng mới được tạo ra:

>>> L = [1, 2, 3] 
>>> id(L) 
152678284 
>>> L.append(4) 
>>> id(L) 
152678284 

>>> L = [1, 2, 3] 
>>> id(L) 
152680524 
>>> L = L + [4] 
>>> id(L) 
152678316 
+0

@Aaron Có, bạn nói đúng. Đã sửa. –

0

Trong ví dụ đầu tiên bạn KL tên biến tham khảo cùng một đối tượng, vì vậy khi bạn gọi một phương pháp đột biến đối tượng đó, thay đổi rõ ràng được nhìn thấy thông qua cả hai tài liệu tham khảo. Trong ví dụ thứ hai, toán tử + gọi list.__add__ trả về đối tượng mới (nối hai danh sách) và L tên bây giờ đề cập đến đối tượng mới này, trong khi K vẫn còn nguyên.

+0

Vì vậy, khi tôi gán một biến cho một danh sách nó sẽ được tham chiếu đến đối tượng? Đây có phải là vì danh sách thực sự không phải là danh sách mà là một tham chiếu không? Tôi nghĩ bây giờ tôi đã hiểu. Điều này có thể xảy ra với danh sách và từ điển phải không? Bởi vì chúng thực sự là cả hai tham chiếu đến chính đối tượng đó. Cảm ơn bạn rất nhiều vì đã làm rõ. Những điều tôi đọc ở đâu đó chỉ là người tranh luận và không thực sự nói rằng sử dụng toán tử + đã gọi ra danh sách .__ add__ Cảm ơn (^_^) – aitee

+0

Bạn có suy nghĩ gì về + =? – aitee

+0

Không chỉ 'danh sách' và' dict', * mọi thứ * trong Python là một đối tượng. Tất cả các đối tượng được lưu trữ trong heap và tất cả các biến chỉ là tham chiếu. Nhưng một số đối tượng là bất biến (như 'int' và 'string'), và một số đối tượng bất biến được tập trung, vì vậy bạn không nhận được hành vi khó hiểu này. Bạn phải phân biệt giữa sửa đổi dữ liệu và sửa đổi ánh xạ giữa tên biến và dữ liệu. Sau đó là những gì sẽ xảy ra khi bạn gán một cái gì đó cho một biến. Tuy nhiên, lưu ý rằng bản đồ biến đó có thể là một phần của dữ liệu nếu nó đại diện cho các thành viên của một đối tượng. – rkhayrov

1

Nếu bạn tò mò về bytecode:

>>> def L_app(): 
...  L.append(4) 
... 
>>> def L_add(): 
...  L = L + [ 4 ] 
... 
>>> def L_add_inplace(): 
...  L += [ 4 ] 
... 
>>> dis.dis(L_app) 
    2   0 LOAD_GLOBAL    0 (L) 
       3 LOAD_ATTR    1 (append) 
       6 LOAD_CONST    1 (4) 
       9 CALL_FUNCTION   1 
      12 POP_TOP 
      13 LOAD_CONST    0 (None) 
      16 RETURN_VALUE 
>>> dis.dis(L_add) 
    2   0 LOAD_FAST    0 (L) 
       3 LOAD_CONST    1 (4) 
       6 BUILD_LIST    1 
       9 BINARY_ADD 
      10 STORE_FAST    0 (L) 
      13 LOAD_CONST    0 (None) 
      16 RETURN_VALUE 
>>> dis.dis(L_add_inplace) 
    2   0 LOAD_FAST    0 (L) 
       3 LOAD_CONST    1 (4) 
       6 BUILD_LIST    1 
       9 INPLACE_ADD 
      10 STORE_FAST    0 (L) 
      13 LOAD_CONST    0 (None) 
      16 RETURN_VALUE 
+0

trường hợp thú vị nhất là dạng '+ ='. – aaronasterling

+0

Mặc dù không mới để lập trình, tôi khá mới với python. Điều này (bytecode) không có nhiều ý nghĩa với tôi. Mã bytecode thường có mã thấp hơn ngôn ngữ, gần mã máy hơn và chạy nhanh hơn không? Có lẽ tôi đã hiểu sai. – aitee

+0

@aaronasterling: thực sự thú vị! Thêm. @aitee: đúng vậy. 'dis' in ra một biểu diễn 'đẹp' của các bytecode mà một hàm Python được biên dịch. Điều này thường hữu ích khi nhìn thấy những gì đang diễn ra "dưới mui xe". – katrielalex

Các vấn đề liên quan