2013-09-28 39 views
9

Các official Python docs nói rằng bằng cách sử dụng các nhà điều hành cắt và gán trong Python làm cho một bản sao nông của danh sách thái lát.Hoạt động cắt có cho tôi bản sao sâu hoặc nông không?

Nhưng khi tôi viết mã ví dụ:

o = [1, 2, 4, 5] 
p = o[:] 

Và khi tôi viết:

id(o) 
id(p) 

tôi nhận được id khác nhau và cũng phụ thêm một một danh sách không phản ánh trong danh sách khác. Không phải là nó tạo ra một bản sao sâu hoặc là có một nơi nào đó tôi đang đi sai?

Trả lời

14

Bạn đang tạo bản sao cạn vì các giá trị lồng nhau không được sao chép, chỉ được tham chiếu. Bản sao sâu cũng sẽ tạo bản sao của các giá trị được tham chiếu trong danh sách.

Demo:

>>> lst = [{}] 
>>> lst_copy = lst[:] 
>>> lst_copy[0]['foo'] = 'bar' 
>>> lst_copy.append(42) 
>>> lst 
[{'foo': 'bar'}] 
>>> id(lst) == id(lst_copy) 
False 
>>> id(lst[0]) == id(lst_copy[0]) 
True 

Ở đây, từ điển lồng nhau không được sao chép; nó chỉ được tham chiếu bởi cả hai danh sách. Phần tử mới 42 không được chia sẻ.

Hãy nhớ rằng tất cả mọi thứ trong Python là một đối tượng, và tên và danh sách các yếu tố chỉ là tham chiếu đến các đối tượng đó. Bản sao danh sách tạo danh sách mới bên ngoài, nhưng danh sách mới chỉ nhận được tham chiếu đến các đối tượng chính xác chính xác.

Một bản sao sâu thích hợp tạo ra các bản sao mới của mỗi và mọi đối tượng chứa trong danh sách, đệ quy:

>>> from copy import deepcopy 
>>> lst_deepcopy = deepcopy(lst) 
>>> id(lst_deepcopy[0]) == id(lst[0]) 
False 
+0

Sau đó, tại sao tôi nhận được các id khác nhau – user2528042

+1

@ user2528042: Bởi vì * đối tượng danh sách bên ngoài * không giống nhau. –

+0

@ user2528042 Vì danh sách gốc * được sao chép * vào một đối tượng mới. Tất cả các phần tử bên trong không được sao chép, vì vậy nếu danh sách chứa một đối tượng có thể thay đổi (ints không thể thay đổi được), thì đối tượng sẽ thay đổi nó trong cả bản gốc và danh sách sao chép vì cả hai đều có một bản sao tham chiếu đến cùng một đối tượng. – poke

5

Bạn nên biết rằng các xét nghiệm sử dụng is hoặc id thể gây hiểu nhầm cho dù một bản sao y đang được thực hiện với bất biến và interned các đối tượng như chuỗi, số nguyên và bộ dữ liệu có chứa bất biến.

Hãy xem xét một dễ hiểu ví dụ về chuỗi thực tập nội trú:

>>> l1=['one'] 
>>> l2=['one'] 
>>> l1 is l2 
False 
>>> l1[0] is l2[0] 
True 

Bây giờ tạo một bản sao cạn của l1 và kiểm tra chuỗi bất biến:

>>> l3=l1[:] 
>>> l3 is l1 
False 
>>> l3[0] is l1[0] 
True 

Bây giờ tạo một bản sao của chuỗi chứa bởi l1[0] :

>>> s1=l1[0][:] 
>>> s1 
'one' 
>>> s1 is l1[0] is l2[0] is l3[0] 
True    # they are all the same object 

T ry một deepcopy nơi mọi yếu tố cần được sao chép:

>>> from copy import deepcopy 
>>> l4=deepcopy(l1) 
>>> l4[0] is l1[0] 
True 

Trong mỗi trường hợp, chuỗi 'one' đang được thực tập nội trú vào bộ nhớ cache nội bộ Python của chuỗi bất biến và is sẽ cho thấy rằng họ đều giống nhau (họ có cùng id) . Nó được thực hiện và phụ thuộc phiên bản của những gì được interned và khi nào, vì vậy bạn không thể phụ thuộc vào nó. Nó có thể là một bộ nhớ đáng kể và nâng cao hiệu suất.

Bạn có thể buộc một ví dụ mà không được thực tập nội trú ngay lập tức:

>>> s2=''.join(c for c in 'one') 
>>> s2==l1[0] 
True 
>>> s2 is l1[0] 
False 

Và sau đó bạn có thể sử dụng Python intern function gây rằng chuỗi để tham khảo các đối tượng lưu trữ nếu tìm thấy:

>>> l1[0] is s2 
False 
>>> s2=intern(s2) 
>>> l1[0] is s2 
True 

Tương tự áp dụng cho các bộ dữ liệu không thay đổi:

>>> t1=('one','two') 
>>> t2=t1[:] 
>>> t1 is t2 
True 
>>> t3=deepcopy(t1) 
>>> t3 is t2 is t1 
True 

An d danh sách có thể thay đổi các immutables (như số nguyên) có thể có các thành viên danh sách mai táng:

>>> li1=[1,2,3] 
>>> li2=deepcopy(li1) 
>>> li2 == li1 
True 
>>> li2 is li1 
False 
>>> li1[0] is li2[0] 
True 

Vì vậy, bạn có thể sử dụng các hoạt động python mà bạn biết sẽ sao chép một cái gì đó nhưng kết quả cuối cùng là một tham chiếu đến một đối tượng bất biến thực tập nội trú. Bài kiểm tra is chỉ là một bài kiểm tra có tính dispositive của một bản sao được thực hiện NẾU các mục có thể thay đổi được.

+0

Trên Python 3, 'intern' đã được chuyển sang mô-đun' sys', vì vậy bạn cần phải thực hiện 'import sys; s = sys.intern (s) ' – dawg

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