Đôi khi, bạn sẽ muốn sao chép hoàn toàn một đối tượng nhưng cũng có lúc muốn dùng nó để làm tài liệu tham khảo. Lúc này, bạn cần tới Shallow và Deep Copy khi lập trình bằng Python.
Python cung cấp một số phương pháp tiếp cận hiệu quả để quản lý dữ liệu. Hiểu các khái niệm shallow và deep copy 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 và deep copy đều cho phép bạn tạo bản sao cấu trúc dữ liệu, nhưng chúng hoạt động khác nhau với dữ liệu lồng nhau.
Dùng Shallow Copy
Shallow copy hoạt động bằng cách tạo bản sao cấu trúc hàng đầu của đối tượng gốc. Điều đó có nghĩa, nếu đối tượng gốc chứa các đối tượng lồng nhau, bản sao sẽ tham chiếu những đối tượng lồng nhau giống như đối tượng gốc. Nói cách khác, tạo shallow copy của một đối tượng sẽ sao chép cấu trúc ngoài cùng của nó, không phải bất kỳ đối tượng lồng nhau mà nó có thể chứa.
Để triển khai một shallow copy trong Python, bạn có thể dùng hàm copy() của mô đun sao chép hay phương thức .copy() trên đối tượng.
Hãy xem xét 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"The main list: {main_list}")
print(f"The shallow copy list: {shallow_copy}")
Ở 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 sao chép tạo một bản sao main_list, trong đó code chứa biến khác, shallow_copy.
Bất kỳ thay đổi bạn thực hiện trên danh sách lồng nhau shallow_copy đều trực tiếp ảnh hưởng tới main_list… Những thay đổi này cho biết 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, tạo ra những thay đổi áp dụng trong cả main_list.
Trong khi đó, bất kỳ thay đổi được thực hiện tới các mục bên ngoài (số nguyên) ở shallow_copy hoặc main_list sẽ chỉ ảnh hưởng tới phiên bản đó. Các mục bên ngoài đều là giá trị độc lập theo đúng nghĩa, không phải đơn thuần 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"The main list: {main_list}")
print(f"The shallow copy list: {shallow_copy}")
Kết quả này chứng minh rằng cả 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"The main dictionary, {dict1}")
print(f"The shallow copy dictionary, {dict2}")
Thay đổi được thực hiện tới từ điển lồng nhau của dict1 ảnh hưởng tới cả dict1 và dict2. Trong khi đó, những thay đổi tới các mục bên ngoài của dict1 chỉ ảnh hưởng tới nó.
Dùng Deep Copy
Thay vì tham chiếu tới các đối tượng lồng nhau của bản sao gốc, một Deep Copy tạo ra một bản sao tách biệt hoàn toàn với đối tượng gốc và các đối tượng được lồng vào nó. Chỉnh sửa deep copy sẽ không ảnh hưởng tới đối tượng gốc và ngược lại. Chúng thật sự là những giá trị riêng biệt.
Để tạo deep copy trong Python, dùng hàm deepcopy() của mô đun bản sao.
Xem xét 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"The main list: {main_list}")
print(f"The deep copy list: {deep_copy}")
Tại đây, code này triển khai một deep copy của main_list, tạo một bản sao độc lập tên deep_copy.
Khi chỉnh sửa danh sách lồng nhau hay các mục bên ngoài trong deep_copy, thay đổi của bạn không ảnh hưởng tới danh sách gốc và ngược lại. Điều này cho thấy danh sách lồng nhau hay 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 class Python và tạo một phiên bản của class.
Đây là ví dụ về cách tạo một đối tượng đơn giản từ class 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}', author='{self.authors}', \
price='{self.price}')"
Giờ, tạo cả hai shallow copy và deep copy của một phiên bản class Book bằng mô đun copy.
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 đối tượng bên trong giống nhau (danh sách tác giả) như đối tượng gốc (book1). Vì thế, một thay đổi tới các tác giả của gốc ảnh hưởng tới cả hai trường hợp (book1 và book2), còn thay đổi tới mục bên ngoài (price) chỉ ảnh hưởng tới đối tượng gốc (book1).
Nó cách khác, một deep copy tạo bản sao độc lập của đối tượng gốc, bao gồm bản sao tất cả đối tượng nằm bên trong nó.
# 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 đối tượng gốc (book1) không ảnh hưởng tới nó.
Những trường hợp sử dụng shallow copy và deep copy
- Dùng shallow copy nếu muốn sao chép một đối tượng phức tạp mà không tạo 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.
- 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à được sao chép.
- 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 tới đối tượng gốc. Điều này tạo bản sao độc lập của các đối tượng được lồng nhau, đảm bảo mọi thay đổi tới bản sao không áp dụng tới đố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, chủ yếu 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. Hi vọng bài viết hữu ích với các bạn.