Trong Python, một biến chỉ là tham chiếu (reference) đến đối tượng. Do đó khi gán cho biến khác, nó không sao chép đối tượng, mà hoạt động như một tham chiếu khác đến cùng đối tượng. Điều này có thể kiểm tra bằng hàm id().
>>> L1 = [1, 2, 3]
>>> L2 = L1
>>> id(L1), id(L2)
(2165544063496, 2165544063496)
Kết quả code trên cho thấy id() cho cả hai đối tượng list giống nhau, nghĩa là cả hai tham chiếu đến cùng đối tượng. L2 được gọi là shallow copy (bản sao nông) của L1. Vì cả hai tham chiếu cùng đối tượng, bất kỳ thay đổi nào trong cái này sẽ phản ánh trong cái kia.
>>> L1 = [1, 2, 3]
>>> L2 = L1
>>> L2[1] = 100
>>> L1, L2
([1, 100, 3], [1, 100, 3])
Trong ví dụ trên, mục tại chỉ số 1 của L2 bị thay đổi. Chúng ta thấy thay đổi này xuất hiện trong cả hai.
Khi một đối tượng hoàn toàn mới được tạo với phép sao chép, với các bản sao của đối tượng lồng nhau cũng được thêm đệ quy, bản sao đó gọi là deep copy (bản sao sâu).
Module copy của thư viện chuẩn Python cung cấp hai phương thức:
copy.copy()– tạo shallow copycopy.deepcopy()– tạo deep copy
Shallow copy tạo đối tượng mới và lưu tham chiếu của các phần tử gốc nhưng không tạo bản sao của đối tượng lồng nhau. Nó chỉ sao chép tham chiếu của đối tượng lồng nhau. Kết quả là quá trình sao chép không đệ quy.
>>> import copy
>>> L1 = [[1, 2, 3], [4, 5, 6]]
>>> L2 = copy.copy(L1)
>>> L1, L2
([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]])
>>> L2.append([10, 10, 10])
>>> L1, L2
([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6], [10, 10, 10]])
Ở đây L1 là nested list và L2 là shallow copy của nó. Mặc dù tham chiếu đến đối tượng list cha được sao chép vào L2, các phần tử lồng nhau không được sao chép. Do đó, khi chúng ta append list khác vào L2, nó không phản ánh trong L1.
Tuy nhiên, nếu chúng ta thử sửa đổi phần tử trong phần tử con, hiệu ứng sẽ thấy trong cả hai list:
>>> L1 = [[1, 2, 3], [4, 5, 6]]
>>> L2 = copy.copy(L1)
>>> L1, L2
([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]])
>>> L2[1][0] = 100
>>> L1, L2
([[1, 2, 3], [100, 5, 6]], [[1, 2, 3], [100, 5, 6]])
Deep copy tuy nhiên, tạo đối tượng mới và đệ quy thêm bản sao của đối tượng lồng nhau cũng có mặt trong các phần tử gốc.
Trong ví dụ sau, L2 là deep copy của L1. Bây giờ nếu chúng ta thay đổi bất kỳ phần tử nào của list con, điều này sẽ không hiện trong list kia.
>>> L1 = [[1, 2, 3], [4, 5, 6]]
>>> L2 = copy.deepcopy(L1)
>>> L1, L2
([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]])
>>> L2[1][0] = 100
>>> L1, L2
([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [100, 5, 6]])
Hiệu ứng của deep copy do đó được xác minh bởi kết quả đầu ra.