2013-07-05 75 views
18

Hy vọng ai đó có thể giúp tôi ở đây.Python: sao chép danh sách trong danh sách

Tôi rất mới với Python và tôi đang cố gắng tìm ra những gì tôi đang làm sai.

Tôi đã tìm kiếm và phát hiện ra rằng các biến Python có thể được liên kết để thay đổi một biến khác và tôi đã thực hiện nhiều thử nghiệm với hàm id() để nắm bắt khái niệm này. Nhưng tôi dường như đã tìm thấy một ngoại lệ Tôi hy vọng một người nào đó có thể giải thích ...

trước tiên, các công trình sau đây dự kiến ​​sẽ tạo một bản sao độc lập của danh sách.

>>> a = [0,0] 
>>> b = a[:] 
>>> print a is b 
False 
>>> b[0]=1 
>>> print a 
[0,0] 
>>> print b 
[1,0] 

Nhưng nếu tôi thay đổi điều này một chút để a là danh sách trong danh sách nó thay đổi ...

>>> a = [[0,0],[0,0]] 
>>> b = a[:] 
>>> print a is b 
False 
>>> b[0][0]=1 
>>> print a 
[[1, 0], [0, 0]] 
>>> print b 
[[1, 0], [0, 0]] 

Bây giờ chúng ta thấy rằng bất kỳ bản cập nhật của b cũng sẽ áp dụng cho a, nhưng nhưng các kết quả của print a is b trả về False ?? Tôi đã kiểm tra điều này chống lại id() là tốt, tất cả mọi thứ nói rằng họ là độc lập với nhau, nhưng khi tôi cập nhật một giống nhau áp dụng cho khác ??

Mọi người có thể giải thích điều này không ??

Lưu ý rằng tôi đang chạy các số http://labs.codecademy.com/#:workspace, vì vậy suy nghĩ đầu tiên của tôi là nó chỉ là một lỗi trên trang web của họ, nhưng tôi không biết?

EDIT:

CẢM ƠN TẤT CẢ các câu trả lời tuyệt vời cho đến nay. Nhanh quá! Tôi biết điều này có thể đã được hỏi trước nhưng nó là điều khó khăn để tìm kiếm.

Vì tất cả các câu trả lời là chính xác, tôi sẽ đợi một ngày trước khi đánh dấu. bất cứ ai có hầu hết +1 sẽ nhận được dấu ấn :)

+2

liên quan: http://stackoverflow.com/questions/3119901/python-deepcopylist-vs-new-list-old-list –

Trả lời

18

b = a[:] tạo ra một shallow copy của a, vì vậy việc thay đổi danh sách có thể thay đổi trong phạm vi b vẫn ảnh hưởng những danh sách cùng trong a.

Nói cách khác, ab không trỏ đến cùng một danh sách (đó là lý do a is not b), nhưng đúng hơn là để hai danh sách khác nhau mà cả hai chứa cùng hai danh sách. Bạn thay đổi một trong các danh sách này qua số b[0][0] = 1 và thay đổi đó hiển thị trong a.

Bạn nói rằng bạn đang chơi xung quanh với id(), vì vậy hãy xem này:

>>> a = [[0,0],[0,0]] 
>>> b = a[:] 
>>> id(a) 
2917280     # <----+ 
>>> id(b)     #  |----- different! 
2771584     # <----+ 
>>> id(a[0]), id(a[1]) 
(2917320, 2917360)   # <----+ 
>>> id(b[0]), id(b[1])  #  |----- same! 
(2917320, 2917360)   # <----+ 
13

Bạn cần phải thực hiện một deepcopy danh sách của bạn. a[:] chỉ làm cho một bản sao cạn - see docs

Bạn có thể sử dụng chức năng copy.deepcopy:

>>> import copy 
>>> a = [[0,0],[0,0]] 
>>> b = copy.deepcopy(a) 
>>> b 
[[0, 0], [0, 0]] 
>>> b[0][0]=1 
>>> a 
[[0, 0], [0, 0]] 
4

a là một danh sách liệt kê.Khi bạn thực hiện b=a[:], bạn tạo danh sách mới, nhưng sao chép các phần tử. Vì vậy, b là một danh sách khác, nhưng các phần tử (danh sách con) giống nhau.

3

Trong cả hai trường hợp, bạn tạo danh sách độc lập. Vì vậy, a is b luôn luôn là sai.

Trong trường hợp đầu tiên, bạn đặt một số giá trị khác vào một trong các danh sách.

Trong trường hợp thứ hai, cả hai danh sách của bạn đều có cùng giá trị.

Nó là như thể bạn sẽ viết

l = [] 
a = [l, l] 
b = [l, l] 

a is not b, và tuy vậy chúng chứa cùng một dữ liệu.

Nếu bạn sửa đổi l bây giờ, sự thay đổi này có thể nhìn thấy qua tất cả các a[0], a[1], b[0]b[1].

3

Mặc dù a is b trả về False, a[0] is b[0] trả về True. Vì vậy, khi bạn thay đổi b[0] bạn đang thiết phải thay đổi a[0]

>>> a = [[0,0],[0,0]] 
>>> b = a[:] 

>>> # a[0] is b[0] 
>>> print a[0] is b[0] 
True 

>>> a.append('more stuff') 
>>> print a 
[[0, 0], [0, 0], 'more stuff'] 
>>> print b 
[[0, 0], [0, 0]] 
6

Tôi tin rằng cách đơn giản nhất để có được những gì đang xảy ra là sử dụng một hình ảnh đại diện (ý tưởng của đại diện này không phải của tôi, mặc dù tôi thích nó).

Trước hết bạn phải hiểu rằng trong python chỉ có tài liệu tham chiếu cho đối tượng. Các đối tượng tự sống riêng rẽ với nhau. Ví dụ: danh sách [0, 1] là một đối tượng danh sách chứa tham chiếu đối tượng 0 và đối tượng 1. Tham chiếu là một loại liên kết . Điều này khác với các biến trong các ngôn ngữ khác, vì các biến thường là các vị trí bộ nhớ nơi bạn đặt mọi thứ. Trong python một "biến", tức là một định danh, chỉ đơn giản là một "tên" (= tham chiếu) cho một đối tượng.

Để hiểu điều này, hãy hình dung mối quan hệ giữa các đối tượng bằng phép ẩn dụ: Giả sử đối tượng là đá nặng trên biển, được liên kết với nhau bằng dây thừng và móc (¿). Trên bề mặt của biển sống các định danh đề cập đến các đối tượng. Các định danh là phao ngăn chặn các đối tượng chìm trong sâu (nơi họ nói, quái vật biển (hay còn gọi là Garbage Collector) sẽ phá hủy chúng).

Ví dụ, chúng ta có thể đại diện cho tình trạng này:

a = [0, 1] 

Với sơ đồ sau đây:

  ___ 
     ( ) 
~~~~~~~~(a)~~~~~~~~ 
     (___) 
o  ¿  o 
      |  O 
      | o 
      | 
      | 
    +------+-------+ 
    | [ ¿ , ¿ ] | 
    +----|-----|---+ 
     |  | 
     |  | 
    o |  | 
O  |  | 
     |  | 
     +-+-+ +-+-+ 
     | 0 | | 1 | 
     +---+ +---+ 

o     O o 
    ) 
    ()    o 
))()  ((
(()((  ()) 

Như bạn có thể thấy các định danh ađề cập, tức là được liên kết với một sợi dây thừng, vào đối tượng danh sách. Đối tượng danh sách có hai vị trí, mỗi vị trí chứa liên kết được kết nối với các đối tượng 01.

Bây giờ, nếu chúng ta đã làm:

b = a 

Từ định danh b sẽ tham khảo cùng đối tượng của a:

  ___     ___ 
      ( )    ( ) 
~~~~~~~~~~~(a)~~~~~~~~~~~~~~~(b)~~~~~~~~~~~~~~~~ 
      (___)    (___) 
      ¿     ¿ 
       \    /
    o   \    /   o 
    o    \   /   o 
       -------+------- 
    O   | [ ¿ , ¿ ] |    O 
       ----|-----|---- 
        |  | 
        +-+-+ +-+-+ 
     o  | 0 | | 1 | 
        +---+ +---+    o 
    O 
     o        O 
             o 


       ) 
      ) (     ) (
     (( )(    (( ) 
     ()) () (   ()) () 

Khi bạn, thay vào đó, làm một bản sao cạn của a, qua:

b = a[:] 

Một danh sách mới được tạo ra, và các yếu tố của nó là bản của tài liệu tham khảo để các đối tượng được gọi bằng a, tức là bạn đã thực hiện các bản sao của dây thừng, nhưng họ trỏ đến các yếu tố giống nhau:

   ___     ___ 
       ( )    ( ) 
    ~~~~~~~~~~~(a)~~~~~~~~~~~~~~~(b)~~~~~~~~~~~~~~~~ 
       (___)    (___) 
    O   ¿     ¿    o 
       |     | 
     o   |     | 
       |     | 
      -------+------  ------+------- 
     | [ ¿ , ¿ ] |  | [ ¿ , ¿ ] | 
      ----|----|----  ----|----|---- 
       | |    | | 
       \ \   //
       \ \   //
       \ \  //   o 
    o    \ \  //   o 
        \ \ //    o 
     o   \ \ //
        \ \//   o 
    O     \ X /
         \/\/
         \/ \/ 
         |  | 
         |  | 
         |  | 
         +-+-+ +-+-+ 
         | 0 | | 1 | 
         +---+ +---+ 



       ) 
      ( (    )  (
    )( ) ) )   (( ) ) ) 
    () () ( ( (  ()) () ( ( (

Vì các số nguyên không thay đổi, không có sự khác biệt giữa việc sử dụng bản sao hoặc cùng một đối tượng giống nhau, nhưng khi bạn thay thế số nguyên bằng list s, là có thể thay đổi, bạn sẽ sửa đổi các tham chiếu đến cùng một đối tượng. hành vi bạn thấy.

Nhìn bề ngoài, mã:

a = [[0, 1], [0, 1]] 
b = a[:] 

Kết quả trong:

   ___     ___ 
       ( )    ( ) 
    ~~~~~~~~~~~(a)~~~~~~~~~~~~~~~(b)~~~~~~~~~~~~~~~~ 
       (___)    (___) 
    O   ¿     ¿    o 
       |     | 
     o   |     | 
       |     | 
      -------+------  ------+------- 
     | [ ¿ , ¿ ] |  | [ ¿ , ¿ ] | 
      ----|----|----  ----|----|---- 
       |  \   / | 
       |  \   / | 
       |  \  /  | 
       |  \  /  | 
       |   \ /  | 
       |   \ /  | 
       |   \/   | 
       |   X   | 
       |   /\   | 
       |  / \   | 
       |  / \   | 
       |  /  \  | 
       |  /  \  | 
       | /   \  | 
       |  |    \  | 
       |  |    | | 
     +----+-----+----+ +-----+----+----+ 
     | [ ¿ , ¿ ] | | [ ¿ , ¿ ] | 
     +----|-----|----+ +----|-----|----+ 
       \  \   / /
       \  \  / /
       \  \  / /
        \  \ / /
        \  \ / /
        \  |/ /
        | |/ /
        | X /
        | /| /
        |/|/
        \/ \/
         Y  Y 
         |  | 
        +-+-+ +-+-+ 
        | 0 | | 1 | 
        +---+ +---+ 


       ) 
     ( (    )  (
    )( ) ) )   (( ) ) ) 
    () () ( ( (  ()) () ( ( (

Lưu ý cách danh sách b đề cập đến các danh sách con cùng a. (chi tiết triển khai: Trình biên dịch bytecode của CPython sẽ tối ưu hóa các biểu thức bằng chữ, để cùng một đối tượng 01 được sử dụng trong cả hai danh sách con. không có tất cả các yếu tố chung).

Bản sao sâu là bản sao tránh chia sẻ các đối tượng giống hệt nhau này.

Ví dụ, sau khi thực hiện:

import copy 
a = [[0, 1], [0, 1]] 
b = copy.deepcopy(a) 

Tình hình là:

   ___            ___ 
       ( )           ( ) 
    ~~~~~~~~~~~(a)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(b)~~~~~~~~~~~~~~~~ 
       (___)           (___) 
    O   ¿            ¿    o 
       |            | 
     o   |            | 
       |            | 
      -------+------         -------+------ 
     | [ ¿ , ¿ ] |         | [ ¿ , ¿ ] | 
      ----|----|----         ----|----|---- 
       |  \           |  \ 
       |  \           |  \ 
       |  \          |  \ 
       |  \          |  \ 
       |   \          |   \ 
       |   \          |   \ 
       |   \         |   \ 
       |   \         |   \ 
       |    \         |    \ 
       |    \         |    \ 
       |    \        |    \ 
       |    \        |    \ 
     +----+----------+ +--+------------+   +----+----------+ +--+------------+ 
     | [ ¿ , ¿ ] | | [ ¿ , ¿ ] |   | [ ¿ , ¿ ] | | [ ¿ , ¿ ] | 
     +----|-----|----+ +----|-----|----+   +----|-----|----+ +----|-----|----+ 
       \  \   / /      \  \   / /
       \  \  / /      \  \  / /
       \  \  / /       \  \  / /
        \  \ / /       \  \ / /
        \  \ / /        \  \ / /
        \  |/ /        \  |/ /
        | |/ /         | |/ /
        | X /         | X /
        | /| /         | /| /
        |/|/          |/|/
        \/ \/          \/ \/
         Y  Y           Y  Y 
         |  |           |  | 
        +-+-+ +-+-+          +-+-+ +-+-+ 
        | 0 | | 1 |          | 0 | | 1 | 
        +---+ +---+          +---+ +---+ 






       )            ) 
     ( (    )  (    ( (    )  (
    )( ) ) )   (( ) ) )  )( ) ) )   (( ) ) ) 
    () () ( ( (  ()) () ( ( ( () () ( ( (  ()) () ( ( (

(Trên thực tế, nó có vẻ như copy.deepcopy là đủ thông minh để tránh sao chép built-in các đối tượng là không thay đổi, chẳng hạn dưới dạng int, long, tuple s đối tượng không thay đổi, v.v. vì vậy tất cả các danh sách con chia sẻ cùng một 01 đối tượng)


Lưu ý rằng các biểu đồ này cũng có thể giúp bạn hiểu cách tính toán tham chiếu hoạt động. Mỗi sợi dây là một tham chiếu, và cho đến khi một đối tượng có một chuỗi các tham chiếu đi đến phao (tức là một mã định danh) nó vẫn còn sống. Khi không có nhiều sợi dây để liên kết một vật thể với phao của bề mặt, thì các vật thể chìm xuống và bị phá hủy bởi bộ thu gom rác.

+4

[Nếu bạn thích nghệ thuật ASCII] (http: // www .asciiflow.com/# Vẽ) –

0

Có một thay thế cho deepcopy tính toán đắt tiền khi bạn đang làm việc với danh sách bên trong danh sách

origvector=[] 
    for ind in range(0, len(testvector)): 
     origvector.append(testvector[ind][:]) 

Trong ví dụ này, "testvector" là một ma trận của n vectơ, mỗi mục có chứa một danh sách ba mục . Như thế này:

{0,1,2}{10,20,30} 
{3,4,5}{40,50,60} 
{6,7,8}{70,80,90} 
Các vấn đề liên quan