Đôi khi, chúng ta muốn sao chép hoàn toàn một đối tượng trong Python, nhưng cũng có lúc chúng ta chỉ muốn một bản sao để làm tài liệu tham khảo. Ở những trường hợp như vậy, chúng ta cần sử dụng các phương pháp sao chép shallow và deep copy.
Python cung cấp một số phương pháp hiệu quả để quản lý dữ liệu. Hiểu được cách hoạt động của shallow và deep copy là rất quan trọng khi làm việc với cấu trúc dữ liệu như danh sách lồng nhau, từ điển hoặc các đối tượng tùy chỉnh.
Cả shallow copy và deep copy đều cho phép chúng ta tạo ra bản sao của cấu trúc dữ liệu, nhưng cách thức hoạt động của chúng với dữ liệu lồng nhau lại khác nhau.
Dùng Shallow Copy
Shallow copy hoạt động bằng cách tạo ra một bản sao của cấu trúc dữ liệu gốc. Điều này có nghĩa là nếu cấu trúc dữ liệu gốc chứa các đối tượng lồng nhau, bản sao sẽ tham chiếu đến những đối tượng lồng nhau giống như đối tượng gốc. Nói cách khác, việc tạo ra một shallow copy của một đối tượng sẽ sao chép cấu trúc ngoài cùng của đối tượng đó, không phải các đối tượng lồng nhau mà nó có thể chứa.
Để thực hiện một shallow copy trong Python, bạn có thể sử dụng hàm copy()
của module sao chép hoặc phương thức copy()
trên đối tượng.
Hãy xem ví dụ về cách làm việc với một danh sách từ điển trong Python:
import copy
main_list = [29, 49, ["Q", "R"]]
shallow_copy = copy.copy(main_list)
# Chỉnh sửa danh sách lồng nhau
shallow_copy[2][0] = 99
main_list[2][1] = 100
print(f"Danh sách gốc: {main_list}")
print(f"Danh sách sao chép: {shallow_copy}")
Trong đoạn code trên, biến main_list
chứa một danh sách các số nguyên và một danh sách lồng nhau (đối tượng lồng nhau) chứa các chữ cái. Hàm copy()
tạo một bản sao của main_list
, gán cho biến khác là shallow_copy
.
Bất kỳ thay đổi nào bạn thực hiện trên danh sách lồng nhau shallow_copy
đều ảnh hưởng trực tiếp đến main_list
. Điều này cho thấy danh sách lồng nhau hoặc bên trong của shallow_copy
chỉ là một tham chiếu tới main_list
, và những thay đổi này được áp dụng cho cả main_list
.
Trong khi đó, bất kỳ thay đổi nào bạn thực hiện đối với các mục bên ngoài (số nguyên) trong shallow_copy
hoặc main_list
chỉ ảnh hưởng đến phiên bản đó. Các mục bên ngoài này là giá trị độc lập theo đúng nghĩa, không phải chỉ là tham chiếu.
import copy
main_list = [29, 49, ["Q", "R"]]
shallow_copy = copy.copy(main_list)
# Chỉnh sửa các mục bên ngoài
shallow_copy[0] = "M"
main_list[1] = "N"
print(f"Danh sách gốc: {main_list}")
print(f"Danh sách sao chép: {shallow_copy}")
Kết quả này chứng minh rằng các mục bên ngoài của danh sách đều độc lập với nhau.
Ý tưởng tương tự cũng được áp dụng khi làm việc với từ điển.
dict1 = {'ten': 10, 'twenty': 20, 'double':{'thirty': 30, 'sixty': 60}}
dict2 = dict1.copy()
# Chỉnh sửa các phần tử bên trong và bên ngoài
dict1['double']['thirty'] = 30.00
dict1['ten'] = 10.00
print(f"Dictionary gốc, {dict1}")
print(f"Dictionary sao chép, {dict2}")
Thay đổi được thực hiện đối với từ điển lồng nhau của dict1
ảnh hưởng đến cả dict1
và dict2
. Trong khi đó, những thay đổi đối với các mục bên ngoài của dict1
chỉ ảnh hưởng đến nó.
Dùng Deep Copy
Thay vì tham chiếu đến các đối tượng lồng nhau của bản sao gốc, một Deep Copy sẽ tạo ra một bản sao hoàn toàn độc lập với đối tượng gốc và các đối tượng được lồng vào đó. Chỉnh sửa deep copy sẽ không ảnh hưởng đến đối tượng gốc và ngược lại. Chúng thực sự là những giá trị riêng biệt.
Để tạo deep copy trong Python, sử dụng hàm deepcopy()
của module sao chép.
Xem ví dụ về cách làm việc với một danh sách:
import copy
main_list = [200, 300, ["I", "J"]]
deep_copy = copy.deepcopy(main_list)
# Chỉnh sửa danh sách bên trong và bên ngoài
deep_copy[2][0] = "K"
main_list[0] = 500
print(f"Danh sách gốc: {main_list}")
print(f"Danh sách deep copy: {deep_copy}")
Ở đây, đoạn code này triển khai một deep copy của main_list
, tạo một bản sao hoàn toàn độc lập có tên là deep_copy
.
Khi bạn chỉnh sửa danh sách lồng nhau hoặc các mục bên ngoài trong deep_copy
, thay đổi của bạn không ảnh hưởng đến danh sách gốc và ngược lại. Điều này cho thấy danh sách lồng nhau hoặc các phần tử bên ngoài không được chia sẻ giữa hai bản sao.
Làm việc với các đối tượng tùy chỉnh
Bạn có thể tạo một đối tượng tùy chỉnh bằng cách xác định một lớp Python và tạo một phiên bản của lớp đó.
Đây là ví dụ về cách tạo một đối tượng đơn giản từ lớp Book
:
class Book:
def __init__(self, title, authors, price):
self.title = title
self.authors = authors
self.price = price
def __str__(self):
return f"Book(title='{self.title}', authors='{self.authors}', price='{self.price}')"
Giờ đây, chúng ta sẽ tạo cả hai shallow copy và deep copy của một phiên bản lớp Book
bằng module sao chép.
import copy
# Tạo đối tượng Book
book1 = Book("How to MakeUseOf Shallow Copy",
["Bobby Jack", "Princewill Inyang"], 1000)
# Tạo một shallow copy
book2 = copy.copy(book1)
# Chỉnh sửa đối tượng gốc
book1.authors.append("Yuvraj Chandra")
book1.price = 50
# Kiểm tra đối tượng
print(book1)
print(book2)
Như bạn có thể thấy, shallow copy (book2
) là một đối tượng mới, nhưng nó tham chiếu đến đối tượng bên trong giống như đối tượng gốc (book1
). Vì vậy, một thay đổi đối với các tác giả của gốc sẽ ảnh hưởng đến cả hai trường hợp (book1
và book2
), còn thay đổi đối với mục bên ngoài (price
) chỉ ảnh hưởng đến đối tượng gốc (book1
).
Sự khác biệt của deep copy là nó tạo ra một bản sao độc lập của đối tượng gốc, bao gồm cả bản sao của các đối tượng lồng vào đó.
# Tạo đối tượng Book
book1 = Book("Why MakeUseOf Deep Copy?",
["Bobby Jack", "Yuvraj Chandra"], 5000)
# Tạo một deep copy
book2 = copy.deepcopy(book1)
# Chỉnh sửa đối tượng gốc
book1.authors.append("Princewill Inyang")
book1.price = 60
# Kiểm tra các đối tượng
print(book1)
print(book2)
Trong trường hợp này, deep copy (book2
) là một đối tượng hoàn toàn độc lập, và chỉnh sửa của đối tượng gốc (book1
) không ảnh hưởng đến nó.
Những trường hợp sử dụng Shallow Copy và Deep Copy
- Sử dụng shallow copy nếu bạn chỉ muốn sao chép một đối tượng phức tạp mà không tạo ra các trường hợp mới của đối tượng lồng nhau. Phương pháp này tiết kiệm bộ nhớ và chạy nhanh hơn deep copy vì nó không sao chép các đối tượng lồng nhau.
- Sử dụng shallow copy để tạo một “snapshot” trạng thái của đối tượng, trong khi vẫn chia sẻ một số dữ liệu cơ bản giữa đối tượng gốc và bản sao.
- Sử dụng deep copy nếu bạn muốn chỉnh sửa bản sao của một đối tượng mà không ảnh hưởng đến đối tượng gốc. Điều này tạo ra một bản sao độc lập của các đối tượng lồng nhau, đảm bảo mọi thay đổi đối với bản sao không ảnh hưởng đến đối tượng gốc.
- Deep copy quan trọng khi bạn cần những bản sao độc lập của cấu trúc dữ liệu lồng nhau, đặc biệt là khi xử lý hệ thống phân cấp đối tượng đệ quy hoặc phức tạp.
Trên đây là những điều bạn cần biết về shallow và deep copy trong Python. Hy vọng bài viết này hữu ích với các bạn.